summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ApiDocs.bp2
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java310
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java47
-rw-r--r--api/Android.bp4
-rw-r--r--cmds/incidentd/src/incidentd_util.cpp39
-rw-r--r--cmds/svc/src/com/android/commands/svc/BluetoothCommand.java58
-rw-r--r--cmds/svc/src/com/android/commands/svc/Svc.java2
-rwxr-xr-xcmds/svc/svc19
-rw-r--r--core/api/current.txt14
-rw-r--r--core/api/module-lib-current.txt6
-rw-r--r--core/api/system-current.txt53
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/app/ActivityOptions.java33
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/ApplicationPackageManager.java93
-rw-r--r--core/java/android/app/KeyguardManager.java36
-rw-r--r--core/java/android/app/LoadedApk.java34
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java6
-rw-r--r--core/java/android/app/WindowConfiguration.java9
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java83
-rw-r--r--core/java/android/app/admin/WifiSsidPolicy.aidl20
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java132
-rw-r--r--core/java/android/content/Intent.java9
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java6
-rw-r--r--core/java/android/content/res/Resources.java5
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java8
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java6
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl2
-rw-r--r--core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl2
-rw-r--r--core/java/android/net/VpnManager.java4
-rw-r--r--core/java/android/os/AppZygote.java15
-rwxr-xr-xcore/java/android/os/Build.java11
-rw-r--r--core/java/android/os/Environment.java3
-rw-r--r--core/java/android/os/OWNERS3
-rw-r--r--core/java/android/os/Parcel.java19
-rw-r--r--core/java/android/os/Process.java36
-rw-r--r--core/java/android/os/vibrator/PrimitiveSegment.java2
-rw-r--r--core/java/android/os/vibrator/RampSegment.java11
-rw-r--r--core/java/android/os/vibrator/StepSegment.java7
-rw-r--r--core/java/android/os/vibrator/VibrationEffectSegment.java37
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/autofill/Dataset.java2
-rw-r--r--core/java/android/service/autofill/Field.java14
-rw-r--r--core/java/android/service/dreams/DreamService.java2
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/NtpTrustedTime.java11
-rw-r--r--core/java/android/view/IWindowManager.aidl6
-rw-r--r--core/java/android/view/IWindowSession.aidl3
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java26
-rw-r--r--core/java/android/view/SurfaceView.java210
-rw-r--r--core/java/android/view/SyncRtSurfaceTransactionApplier.java8
-rw-r--r--core/java/android/view/View.java64
-rw-r--r--core/java/android/view/ViewRootImpl.java151
-rw-r--r--core/java/android/view/WindowLayout.java31
-rw-r--r--core/java/android/view/WindowManager.java10
-rw-r--r--core/java/android/view/WindowManagerGlobal.java8
-rw-r--r--core/java/android/view/WindowlessWindowManager.java7
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnection.aidl22
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl6
-rw-r--r--core/java/android/webkit/WebViewZygote.java5
-rw-r--r--core/java/android/widget/inline/InlineContentView.java5
-rw-r--r--core/java/android/window/SplashScreenView.java11
-rw-r--r--core/java/android/window/SurfaceSyncer.java377
-rw-r--r--core/java/android/window/WindowContainerTransaction.java104
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java48
-rw-r--r--core/java/com/android/internal/os/Zygote.java263
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/server/SystemConfig.java33
-rw-r--r--core/jni/android_media_AudioSystem.cpp20
-rw-r--r--core/jni/android_view_SurfaceControl.cpp1
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp6
-rw-r--r--core/proto/android/hardware/sensorprivacy.proto2
-rw-r--r--core/proto/android/server/biometrics.proto14
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml13
-rw-r--r--core/res/res/values/arrays.xml6
-rw-r--r--core/res/res/values/config.xml12
-rw-r--r--core/res/res/values/ids.xml5
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/strings.xml24
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--data/etc/privapp-permissions-platform.xml7
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java27
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java19
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java111
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java150
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java4
-rw-r--r--location/java/android/location/SatellitePvt.java67
-rw-r--r--media/java/android/media/AudioManager.java98
-rw-r--r--media/java/android/media/AudioSystem.java91
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl2
-rw-r--r--media/jni/soundpool/Stream.cpp94
-rw-r--r--media/jni/soundpool/Stream.h33
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java72
-rw-r--r--native/android/performance_hint.cpp4
-rw-r--r--obex/Android.bp2
-rw-r--r--obex/javax/obex/ApplicationParameter.java162
-rw-r--r--obex/javax/obex/Authenticator.java115
-rw-r--r--obex/javax/obex/BaseStream.java76
-rw-r--r--obex/javax/obex/ClientOperation.java851
-rw-r--r--obex/javax/obex/ClientSession.java616
-rw-r--r--obex/javax/obex/HeaderSet.java710
-rw-r--r--obex/javax/obex/ObexHelper.java1100
-rw-r--r--obex/javax/obex/ObexSession.java224
-rw-r--r--obex/javax/obex/ObexTransport.java113
-rw-r--r--obex/javax/obex/Operation.java183
-rw-r--r--obex/javax/obex/PasswordAuthentication.java79
-rw-r--r--obex/javax/obex/PrivateInputStream.java181
-rw-r--r--obex/javax/obex/PrivateOutputStream.java172
-rw-r--r--obex/javax/obex/ResponseCodes.java326
-rw-r--r--obex/javax/obex/ServerOperation.java861
-rw-r--r--obex/javax/obex/ServerRequestHandler.java287
-rw-r--r--obex/javax/obex/ServerSession.java742
-rw-r--r--obex/javax/obex/SessionNotifier.java128
-rw-r--r--packages/CompanionDeviceManager/res/color/selector.xml2
-rw-r--r--packages/CompanionDeviceManager/res/drawable/dialog_background.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values/themes.xml4
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java4
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java70
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml6
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml7
-rw-r--r--packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml19
-rw-r--r--packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml21
-rw-r--r--packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml19
-rw-r--r--packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml21
-rw-r--r--packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml40
-rw-r--r--packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml24
-rw-r--r--packages/SettingsLib/res/values-v31/styles.xml39
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java239
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java4
-rw-r--r--packages/SettingsLib/tests/integ/AndroidManifest.xml11
-rw-r--r--packages/SettingsLib/tests/integ/res/xml/file_paths.xml (renamed from packages/SystemUI/res/values-w650dp-land/dimens.xml)12
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java299
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/docs/keyguard.md2
-rw-r--r--packages/SystemUI/docs/keyguard/doze.md (renamed from packages/SystemUI/docs/keyguard/aod.md)29
-rw-r--r--packages/SystemUI/proguard.flags2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml1
-rw-r--r--packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml255
-rw-r--r--packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml1
-rw-r--r--packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml248
-rw-r--r--packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml1
-rw-r--r--packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml25
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml4
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml (renamed from packages/SystemUI/res/layout/auth_biometric_face_to_fingerprint_view.xml)5
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_view.xml (renamed from packages/SystemUI/res/layout/auth_biometric_udfps_view.xml)6
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml3
-rw-r--r--packages/SystemUI/res/layout/foreground_service_dungeon.xml61
-rw-r--r--packages/SystemUI/res/layout/foreground_service_dungeon_row.xml43
-rw-r--r--packages/SystemUI/res/layout/media_projection_dialog_title.xml40
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml6
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/dimens.xml7
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/dimens.xml27
-rw-r--r--packages/SystemUI/res/values/config.xml7
-rw-r--r--packages/SystemUI/res/values/dimens.xml10
-rw-r--r--packages/SystemUI/res/values/strings.xml57
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java36
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java245
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java261
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java174
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java304
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java346
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/Utils.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java195
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java206
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java)22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java)208
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java)142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java98
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java220
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java)68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java344
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java360
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java329
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt324
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java231
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java269
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt75
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java)27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java)139
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java30
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java19
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java148
-rw-r--r--services/api/current.txt2
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java57
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java7
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java24
-rw-r--r--services/companion/java/com/android/server/companion/virtual/CameraAccessController.java39
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java32
-rw-r--r--services/companion/java/com/android/server/companion/virtual/PermissionUtils.java16
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java4
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java45
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java86
-rw-r--r--services/core/java/com/android/server/NetworkTimeUpdateService.java41
-rw-r--r--services/core/java/com/android/server/OWNERS3
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerLocal.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java33
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java44
-rw-r--r--services/core/java/com/android/server/am/HostingRecord.java38
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java232
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java35
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java16
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java46
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java11
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java14
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java180
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java131
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java9
-rw-r--r--services/core/java/com/android/server/audio/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java170
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricSensor.java6
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java490
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java65
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java2
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java306
-rw-r--r--services/core/java/com/android/server/infra/AbstractPerUserSystemService.java122
-rw-r--r--services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java139
-rw-r--r--services/core/java/com/android/server/infra/OWNERS3
-rw-r--r--services/core/java/com/android/server/infra/ServiceNameResolver.java71
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java68
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerService.java18
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java18
-rw-r--r--services/core/java/com/android/server/locales/SystemAppUpdateTracker.java249
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java4
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java63
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java44
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java64
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java11
-rw-r--r--services/core/java/com/android/server/pm/Settings.java12
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java3
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java54
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java12
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java6
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java60
-rw-r--r--services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java468
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java510
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java8
-rw-r--r--services/core/java/com/android/server/trust/TrustAgentWrapper.java1
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java56
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java221
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java44
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java28
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java51
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java319
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java29
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java15
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java102
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java49
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java2
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java30
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java126
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java20
-rw-r--r--services/core/java/com/android/server/wm/DragResizeMode.java2
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java68
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java3
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java20
-rw-r--r--services/core/java/com/android/server/wm/InputTarget.java20
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java5
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java27
-rw-r--r--services/core/java/com/android/server/wm/Session.java10
-rw-r--r--services/core/java/com/android/server/wm/Task.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java9
-rw-r--r--services/core/java/com/android/server/wm/Transition.java39
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java55
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java150
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp57
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java83
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java8
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--services/proguard.flags13
-rw-r--r--services/tests/inprocesstests/AndroidTest.xml2
-rw-r--r--services/tests/mockingservicestests/Android.bp2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java360
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt4
-rw-r--r--services/tests/servicestests/AndroidManifest.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java176
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java342
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java401
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java124
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java297
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java117
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java78
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java25
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java14
-rw-r--r--services/tests/wmtests/Android.bp1
-rw-r--r--services/tests/wmtests/AndroidManifest.xml8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java66
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java146
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java223
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java96
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java65
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java221
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java72
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbSerialReader.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java9
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java16
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java51
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl6
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java17
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java40
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java35
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java4
-rw-r--r--test-runner/src/android/test/IsolatedContext.java11
-rw-r--r--tests/AttestationVerificationTest/Android.bp1
-rwxr-xr-xtests/AttestationVerificationTest/AndroidManifest.xml1
-rw-r--r--tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem81
-rw-r--r--tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem30
-rw-r--r--tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem33
-rw-r--r--tests/AttestationVerificationTest/assets/test_root_certs.pem61
-rw-r--r--tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem50
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt161
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt8
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java297
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt175
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt3
-rw-r--r--tests/SurfaceViewSyncTest/Android.bp31
-rw-r--r--tests/SurfaceViewSyncTest/AndroidManifest.xml27
-rw-r--r--tests/SurfaceViewSyncTest/OWNERS1
-rw-r--r--tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml47
-rw-r--r--tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java191
-rw-r--r--tests/TrustTests/Android.bp39
-rw-r--r--tests/TrustTests/AndroidManifest.xml75
-rw-r--r--tests/TrustTests/AndroidTest.xml28
-rw-r--r--tests/TrustTests/OWNERS1
-rw-r--r--tests/TrustTests/README.md40
-rw-r--r--tests/TrustTests/src/android/trust/BaseTrustAgentService.kt47
-rw-r--r--tests/TrustTests/src/android/trust/TrustTestActivity.kt30
-rw-r--r--tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt95
-rw-r--r--tests/TrustTests/src/android/trust/test/LockUserTest.kt68
-rw-r--r--tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt81
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt83
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt105
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt117
626 files changed, 17190 insertions, 16668 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 5b7c125ed46b..7f5d4a3fd504 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -94,7 +94,7 @@ stubs_defaults {
":framework-scheduling-sources",
":framework-sdkextensions-sources",
":framework-statsd-sources",
- ":framework-supplementalprocess-sources",
+ ":framework-sdksandbox-sources",
":framework-tethering-srcs",
":framework-uwb-updatable-sources",
":framework-wifi-updatable-sources",
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index ffa534ec2053..c83ca8c2e31d 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -1377,7 +1377,7 @@ public class BlobStoreManagerService extends SystemService {
}
private boolean isAllowedBlobAccess(int uid, String packageName) {
- return (!Process.isSupplemental(uid) && !Process.isIsolated(uid)
+ return (!Process.isSdkSandboxUid(uid) && !Process.isIsolated(uid)
&& !mPackageManagerInternal.isInstantApp(packageName, UserHandle.getUserId(uid)));
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 2b0a833e1d1f..ee0f9e8f9658 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -37,7 +37,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.IUidObserver;
-import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -166,28 +165,6 @@ public final class QuotaController extends StateController {
public long inQuotaTimeElapsed;
/**
- * The time after which the app will be under the bucket quota and can start running
- * low priority jobs again. This is only valid if
- * {@link #executionTimeInWindowMs} >=
- * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityLow}),
- * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
- * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
- * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
- */
- public long inQuotaTimeLowElapsed;
-
- /**
- * The time after which the app will be under the bucket quota and can start running
- * min priority jobs again. This is only valid if
- * {@link #executionTimeInWindowMs} >=
- * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityMin}),
- * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
- * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
- * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
- */
- public long inQuotaTimeMinElapsed;
-
- /**
* The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
* in the elapsed realtime timebase.
*/
@@ -227,8 +204,6 @@ public final class QuotaController extends StateController {
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
+ "inQuotaTime=" + inQuotaTimeElapsed + ", "
- + "inQuotaTimeLow=" + inQuotaTimeLowElapsed + ", "
- + "inQuotaTimeMin=" + inQuotaTimeMinElapsed + ", "
+ "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
+ "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", "
+ "rateLimitSessionCountExpirationTime="
@@ -385,24 +360,6 @@ public final class QuotaController extends StateController {
*/
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
- /**
- * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
- * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- private float mAllowedTimeSurplusPriorityLow =
- QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
-
- /**
- * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
- * {@link JobInfo#PRIORITY_MIN min priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- private float mAllowedTimeSurplusPriorityMin =
- QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
-
/** The period of time used to rate limit recently run jobs. */
private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
@@ -828,8 +785,7 @@ public final class QuotaController extends StateController {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
return getTimeUntilQuotaConsumedLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
- jobStatus.getEffectivePriority());
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
}
// Expedited job.
@@ -919,8 +875,7 @@ public final class QuotaController extends StateController {
return isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket,
- jobStatus.getEffectivePriority());
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
}
@GuardedBy("mLock")
@@ -937,7 +892,7 @@ public final class QuotaController extends StateController {
@VisibleForTesting
@GuardedBy("mLock")
boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
- final int standbyBucket, final int priority) {
+ final int standbyBucket) {
if (!mIsEnabled) {
return true;
}
@@ -945,18 +900,9 @@ public final class QuotaController extends StateController {
if (isQuotaFreeLocked(standbyBucket)) return true;
- final long minSurplus;
- if (priority <= JobInfo.PRIORITY_MIN) {
- minSurplus = (long)
- (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityMin);
- } else if (priority <= JobInfo.PRIORITY_LOW) {
- minSurplus = (long)
- (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityLow);
- } else {
- minSurplus = 0;
- }
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
- return getRemainingExecutionTimeLocked(stats) > minSurplus
+ // TODO: use a higher minimum remaining time for jobs with MINIMUM priority
+ return getRemainingExecutionTimeLocked(stats) > 0
&& isUnderJobCountQuotaLocked(stats, standbyBucket)
&& isUnderSessionCountQuotaLocked(stats, standbyBucket);
}
@@ -1074,8 +1020,7 @@ public final class QuotaController extends StateController {
* job is running.
*/
@VisibleForTesting
- long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName,
- @JobInfo.Priority int jobPriority) {
+ long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
final long nowElapsed = sElapsedRealtimeClock.millis();
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(
packageName, userId, nowElapsed);
@@ -1096,15 +1041,11 @@ public final class QuotaController extends StateController {
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
- final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(standbyBucket, jobPriority);
+ final long allowedTimePerPeriodMs = mAllowedTimePerPeriodMs[standbyBucket];
final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
- if (maxExecutionTimeRemainingMs < 0) {
- return 0;
- }
-
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
@@ -1112,10 +1053,6 @@ public final class QuotaController extends StateController {
sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
}
- if (allowedTimeRemainingMs < 0) {
- return 0;
- }
-
// Need to check both max time and period time in case one is less than the other.
// For example, max time remaining could be less than bucket time remaining, but sessions
// contributing to the max time remaining could phase out enough that we'd want to use the
@@ -1127,21 +1064,6 @@ public final class QuotaController extends StateController {
sessions, startWindowElapsed, allowedTimeRemainingMs));
}
- private long getAllowedTimePerPeriodMs(int standbyBucket, @JobInfo.Priority int jobPriority) {
- return getAllowedTimePerPeriodMs(mAllowedTimePerPeriodMs[standbyBucket], jobPriority);
- }
-
- private long getAllowedTimePerPeriodMs(long initialAllowedTime,
- @JobInfo.Priority int jobPriority) {
- if (jobPriority <= JobInfo.PRIORITY_MIN) {
- return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityMin));
- }
- if (jobPriority <= JobInfo.PRIORITY_LOW) {
- return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityLow));
- }
- return initialAllowedTime;
- }
-
/**
* Calculates how much time it will take, in milliseconds, until the quota is fully consumed.
*
@@ -1299,15 +1221,10 @@ public final class QuotaController extends StateController {
stats.sessionCountInWindow = 0;
if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) {
// App won't be in quota until configuration changes.
- stats.inQuotaTimeElapsed = stats.inQuotaTimeLowElapsed = stats.inQuotaTimeMinElapsed =
- Long.MAX_VALUE;
+ stats.inQuotaTimeElapsed = Long.MAX_VALUE;
} else {
stats.inQuotaTimeElapsed = 0;
}
- final long allowedTimeMinMs =
- getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_MIN);
- final long allowedTimeLowMs =
- getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_LOW);
final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs;
Timer timer = mPkgTimers.get(userId, packageName);
@@ -1326,25 +1243,13 @@ public final class QuotaController extends StateController {
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs);
}
- if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- nowElapsed - allowedTimeLowMs + stats.windowSizeMs);
- }
- if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- nowElapsed - allowedTimeMinMs + stats.windowSizeMs);
- }
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
final long inQuotaTime = nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = nowElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
}
@@ -1386,23 +1291,9 @@ public final class QuotaController extends StateController {
start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs
+ stats.windowSizeMs);
}
- if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- start + stats.executionTimeInWindowMs - allowedTimeLowMs
- + stats.windowSizeMs);
- }
- if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- start + stats.executionTimeInWindowMs - allowedTimeMinMs
- + stats.windowSizeMs);
- }
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- inQuotaTime);
}
if (i == loopStart
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
@@ -1413,10 +1304,6 @@ public final class QuotaController extends StateController {
if (sessionCountInWindow >= stats.sessionCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- inQuotaTime);
}
}
}
@@ -1706,7 +1593,7 @@ public final class QuotaController extends StateController {
/**
* Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package.
*
- * @return true if at least one job had its bit changed
+ * @return the set of jobs whose status changed
*/
@NonNull
private ArraySet<JobStatus> maybeUpdateConstraintForPkgLocked(final long nowElapsed,
@@ -1719,8 +1606,7 @@ public final class QuotaController extends StateController {
// Quota is the same for all jobs within a package.
final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
- final boolean realInQuota = isWithinQuotaLocked(
- userId, packageName, realStandbyBucket, JobInfo.PRIORITY_DEFAULT);
+ final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
boolean outOfEJQuota = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
@@ -1733,8 +1619,7 @@ public final class QuotaController extends StateController {
changedJobs.add(js);
}
} else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
- && realStandbyBucket == js.getEffectiveStandbyBucket()
- && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
+ && realStandbyBucket == js.getEffectiveStandbyBucket()) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
// individually.
@@ -1798,8 +1683,9 @@ public final class QuotaController extends StateController {
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
if (isWithinEJQuota
- && isWithinQuotaLocked(userId, packageName, realStandbyBucket,
- JobInfo.PRIORITY_MIN)) {
+ && isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
+ // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
+ // that all jobs for the userId-package are within quota.
mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
} else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
@@ -1860,25 +1746,8 @@ public final class QuotaController extends StateController {
standbyBucket);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
- int minPriority = JobInfo.PRIORITY_MAX;
- boolean hasDefPlus = false, hasLow = false, hasMin = false;
- for (int i = jobs.size() - 1; i >= 0; --i) {
- final int priority = jobs.valueAt(i).getEffectivePriority();
- minPriority = Math.min(minPriority, priority);
- if (priority <= JobInfo.PRIORITY_MIN) {
- hasMin = true;
- } else if (priority <= JobInfo.PRIORITY_LOW) {
- hasLow = true;
- } else {
- hasDefPlus = true;
- }
- if (hasMin && hasLow && hasDefPlus) {
- break;
- }
- }
final boolean inRegularQuota =
- stats.executionTimeInWindowMs
- < getAllowedTimePerPeriodMs(standbyBucket, minPriority)
+ stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs[standbyBucket]
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
&& isUnderTimingSessionCountQuota;
@@ -1900,24 +1769,7 @@ public final class QuotaController extends StateController {
long inEJQuotaTimeElapsed = Long.MAX_VALUE;
if (!inRegularQuota) {
// The time this app will have quota again.
- long executionInQuotaTime = Long.MAX_VALUE;
- boolean hasExecutionInQuotaTime = false;
- if (hasMin && stats.inQuotaTimeMinElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeMinElapsed);
- hasExecutionInQuotaTime = true;
- }
- if (hasLow && stats.inQuotaTimeLowElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeLowElapsed);
- hasExecutionInQuotaTime = true;
- }
- if (hasDefPlus && stats.inQuotaTimeElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeElapsed);
- hasExecutionInQuotaTime = true;
- }
- long inQuotaTimeElapsed = 0;
- if (hasExecutionInQuotaTime) {
- inQuotaTimeElapsed = executionInQuotaTime;
- }
+ long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
// App hit the rate limit.
inQuotaTimeElapsed =
@@ -2130,7 +1982,6 @@ public final class QuotaController extends StateController {
private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
private long mStartTimeElapsed;
private int mBgJobCount;
- private int mLowestPriority = JobInfo.PRIORITY_MAX;
private long mDebitAdjustment;
Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
@@ -2153,8 +2004,6 @@ public final class QuotaController extends StateController {
Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
}
// Always maintain list of running jobs, even when quota is free.
- final boolean priorityLowered = mLowestPriority > jobStatus.getEffectivePriority();
- mLowestPriority = Math.min(mLowestPriority, jobStatus.getEffectivePriority());
if (mRunningBgJobs.add(jobStatus) && shouldTrackLocked()) {
mBgJobCount++;
if (mRegularJobTimer) {
@@ -2170,8 +2019,6 @@ public final class QuotaController extends StateController {
invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
}
scheduleCutoff();
- } else if (mRegularJobTimer && priorityLowered) {
- rescheduleCutoff();
}
}
}
@@ -2196,19 +2043,6 @@ public final class QuotaController extends StateController {
&& !isQuotaFreeLocked(standbyBucket)) {
emitSessionLocked(nowElapsed);
cancelCutoff();
- mLowestPriority = JobInfo.PRIORITY_MAX;
- } else if (mRegularJobTimer
- && mLowestPriority == jobStatus.getEffectivePriority()) {
- // Lowest priority doesn't matter for EJ timers.
- final int oldPriority = mLowestPriority;
- mLowestPriority = JobInfo.PRIORITY_MAX;
- for (int i = mRunningBgJobs.size() - 1; i >= 0; --i) {
- mLowestPriority = Math.min(mLowestPriority,
- mRunningBgJobs.valueAt(i).getEffectivePriority());
- }
- if (mLowestPriority != oldPriority) {
- rescheduleCutoff();
- }
}
}
}
@@ -2335,14 +2169,9 @@ public final class QuotaController extends StateController {
}
Message msg = mHandler.obtainMessage(
mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
- final long timeRemainingMs;
- if (mRegularJobTimer) {
- timeRemainingMs = getTimeUntilQuotaConsumedLocked(
- mPkg.userId, mPkg.packageName, mLowestPriority);
- } else {
- timeRemainingMs =
- getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
- }
+ final long timeRemainingMs = mRegularJobTimer
+ ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
+ : getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
if (DEBUG) {
Slog.i(TAG,
(mRegularJobTimer ? "Regular job" : "EJ") + " for " + mPkg + " has "
@@ -2695,19 +2524,26 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
}
- final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
- sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
- if (changedJobs.size() > 0) {
+ long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
+ pkg.packageName);
+ if (timeRemainingMs <= 50) {
+ // Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
- mStateChangedListener.onControllerStateChanged(changedJobs);
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName));
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
+ timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId,
+ pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
}
- mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
}
break;
}
@@ -2717,19 +2553,25 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota.");
}
- final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
- sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
- if (changedJobs.size() > 0) {
+ long timeRemainingMs = getRemainingEJExecutionTimeLocked(
+ pkg.userId, pkg.packageName);
+ if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
- mStateChangedListener.onControllerStateChanged(changedJobs);
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName));
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg);
+ timeRemainingMs = getTimeUntilEJQuotaConsumedLocked(
+ pkg.userId, pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for EJ");
}
- mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
}
break;
}
@@ -2993,12 +2835,6 @@ public final class QuotaController extends StateController {
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting
- static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
- QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_low";
- @VisibleForTesting
- static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
- QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
- @VisibleForTesting
static final String KEY_WINDOW_SIZE_EXEMPTED_MS =
QC_CONSTANT_PREFIX + "window_size_exempted_ms";
@VisibleForTesting
@@ -3130,8 +2966,6 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
- private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
- private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
@@ -3230,22 +3064,6 @@ public final class QuotaController extends StateController {
public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
/**
- * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
- * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
-
- /**
- * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
- * {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running min priority
- * jobs.
- */
- public float ALLOWED_TIME_SURPLUS_PRIORITY_MIN = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
-
- /**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
* WINDOW_SIZE_MS.
@@ -3514,8 +3332,6 @@ public final class QuotaController extends StateController {
case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
- case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
- case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3757,7 +3573,6 @@ public final class QuotaController extends StateController {
KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
KEY_IN_QUOTA_BUFFER_MS,
- KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
KEY_MAX_EXECUTION_TIME_MS,
KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
@@ -3781,12 +3596,6 @@ public final class QuotaController extends StateController {
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
- ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
- properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
- DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
- ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
- properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
- DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
@@ -3865,23 +3674,6 @@ public final class QuotaController extends StateController {
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
- // Low priority surplus should be in the range [0, .9]. A value of 1 would essentially
- // mean never run low priority jobs.
- float newAllowedTimeSurplusPriorityLow =
- Math.max(0f, Math.min(.9f, ALLOWED_TIME_SURPLUS_PRIORITY_LOW));
- if (Float.compare(
- mAllowedTimeSurplusPriorityLow, newAllowedTimeSurplusPriorityLow) != 0) {
- mAllowedTimeSurplusPriorityLow = newAllowedTimeSurplusPriorityLow;
- mShouldReevaluateConstraints = true;
- }
- // Min priority surplus should be in the range [0, mAllowedTimeSurplusPriorityLow].
- float newAllowedTimeSurplusPriorityMin = Math.max(0f,
- Math.min(mAllowedTimeSurplusPriorityLow, ALLOWED_TIME_SURPLUS_PRIORITY_MIN));
- if (Float.compare(
- mAllowedTimeSurplusPriorityMin, newAllowedTimeSurplusPriorityMin) != 0) {
- mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
- mShouldReevaluateConstraints = true;
- }
long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) {
@@ -4081,10 +3873,6 @@ public final class QuotaController extends StateController {
.println();
pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
- pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
- .println();
- pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
- .println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
@@ -4210,16 +3998,6 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
- float getAllowedTimeSurplusPriorityLow() {
- return mAllowedTimeSurplusPriorityLow;
- }
-
- @VisibleForTesting
- float getAllowedTimeSurplusPriorityMin() {
- return mAllowedTimeSurplusPriorityMin;
- }
-
- @VisibleForTesting
@NonNull
int[] getBucketMaxJobCounts() {
return mMaxBucketJobCounts;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index cae6cdcf8f1f..54c3db48231d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -104,6 +104,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.view.Display;
import android.widget.Toast;
@@ -260,6 +261,13 @@ public class AppStandbyController
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+ /**
+ * Set of user IDs and the next time (in the elapsed realtime timebase) when we should check the
+ * apps' idle states.
+ */
+ @GuardedBy("mPendingIdleStateChecks")
+ private final SparseLongArray mPendingIdleStateChecks = new SparseLongArray();
+
// Cache the active network scorer queried from the network scorer service
private volatile String mCachedNetworkScorer = null;
// The last time the network scorer service was queried
@@ -722,7 +730,14 @@ public class AppStandbyController
@Override
public void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ if (userId == UserHandle.USER_ALL) {
+ postOneTimeCheckIdleStates();
+ } else {
+ synchronized (mPendingIdleStateChecks) {
+ mPendingIdleStateChecks.put(userId, mInjector.elapsedRealtime());
+ }
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES).sendToTarget();
+ }
}
@Override
@@ -2374,10 +2389,32 @@ public class AppStandbyController
break;
case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
+ removeMessages(MSG_CHECK_IDLE_STATES);
+
+ long earliestCheck = Long.MAX_VALUE;
+ final long nowElapsed = mInjector.elapsedRealtime();
+ synchronized (mPendingIdleStateChecks) {
+ for (int i = mPendingIdleStateChecks.size() - 1; i >= 0; --i) {
+ long expirationTime = mPendingIdleStateChecks.valueAt(i);
+
+ if (expirationTime <= nowElapsed) {
+ final int userId = mPendingIdleStateChecks.keyAt(i);
+ if (checkIdleStates(userId) && mAppIdleEnabled) {
+ expirationTime = nowElapsed + mCheckIdleIntervalMillis;
+ mPendingIdleStateChecks.put(userId, expirationTime);
+ } else {
+ mPendingIdleStateChecks.removeAt(i);
+ continue;
+ }
+ }
+
+ earliestCheck = Math.min(earliestCheck, expirationTime);
+ }
+ }
+ if (earliestCheck != Long.MAX_VALUE) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES),
+ earliestCheck - nowElapsed);
}
break;
diff --git a/api/Android.bp b/api/Android.bp
index 9e377a01a498..464c1639d25f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -123,7 +123,7 @@ combined_apis {
"framework-scheduling",
"framework-sdkextensions",
"framework-statsd",
- "framework-supplementalprocess",
+ "framework-sdksandbox",
"framework-tethering",
"framework-uwb",
"framework-wifi",
@@ -136,7 +136,7 @@ combined_apis {
system_server_classpath: [
"service-media-s",
"service-permission",
- "service-supplementalprocess",
+ "service-sdksandbox",
],
}
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 150ab9991a2d..ec0b79b34c02 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -184,11 +184,26 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
sigemptyset(&child_mask);
sigaddset(&child_mask, SIGCHLD);
+ // block SIGCHLD before we check if a process has exited
if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
- ALOGW("sigprocmask failed: %s", strerror(errno));
+ ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
return false;
}
+ // if the child has exited already, handle and reset signals before leaving
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid > 0) {
+ ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return false;
+ }
+ } else {
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return true;
+ }
+
+ // wait for a SIGCHLD
timespec ts;
ts.tv_sec = timeout_ms / 1000;
ts.tv_nsec = (timeout_ms % 1000) * 1000000;
@@ -197,7 +212,7 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
// Set the signals back the way they were.
if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
- ALOGW("sigprocmask failed: %s", strerror(errno));
+ ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
if (ret == 0) {
return false;
}
@@ -207,21 +222,21 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
if (errno == EAGAIN) {
errno = ETIMEDOUT;
} else {
- ALOGW("sigtimedwait failed: %s", strerror(errno));
+ ALOGW("*** sigtimedwait failed: %s\n", strerror(errno));
}
return false;
}
- pid_t child_pid = waitpid(pid, status, WNOHANG);
- if (child_pid == pid) {
- return true;
- }
- if (child_pid == -1) {
- ALOGW("waitpid failed: %s", strerror(errno));
- } else {
- ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid);
+ child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid != -1) {
+ ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ } else {
+ ALOGW("*** waitpid failed: %s\n", strerror(errno));
+ }
+ return false;
}
- return false;
+ return true;
}
status_t kill_child(pid_t pid) {
diff --git a/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java b/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java
deleted file mode 100644
index b572ce24390c..000000000000
--- a/cmds/svc/src/com/android/commands/svc/BluetoothCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 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.commands.svc;
-
-import android.bluetooth.BluetoothAdapter;
-import android.os.RemoteException;
-
-public class BluetoothCommand extends Svc.Command {
-
- public BluetoothCommand() {
- super("bluetooth");
- }
-
- @Override
- public String shortHelp() {
- return "Control Bluetooth service";
- }
-
- @Override
- public String longHelp() {
- return shortHelp() + "\n"
- + "\n"
- + "usage: svc bluetooth [enable|disable]\n"
- + " Turn Bluetooth on or off.\n\n";
- }
-
- @Override
- public void run(String[] args) {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (adapter == null) {
- System.err.println("Got a null BluetoothAdapter, is the system running?");
- return;
- }
-
- if (args.length == 2 && "enable".equals(args[1])) {
- adapter.enable();
- } else if (args.length == 2 && "disable".equals(args[1])) {
- adapter.disable();
- } else {
- System.err.println(longHelp());
- }
- }
-}
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 2ed2678bc877..bbad984746a1 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -96,7 +96,7 @@ public class Svc {
// `svc wifi` has been migrated to WifiShellCommand
new UsbCommand(),
new NfcCommand(),
- new BluetoothCommand(),
+ // `svc bluetooth` has been migrated to BluetoothShellCommand
new SystemServerCommand(),
};
}
diff --git a/cmds/svc/svc b/cmds/svc/svc
index 95265e817c1b..a2c9de32b3d0 100755
--- a/cmds/svc/svc
+++ b/cmds/svc/svc
@@ -33,6 +33,25 @@ if [ "x$1" == "xdata" ]; then
exit 1
fi
+# `svc bluetooth` has been migrated to BluetoothShellCommand,
+# simply perform translation to `cmd bluetooth set-bluetooth-enabled` here.
+if [ "x$1" == "xbluetooth" ]; then
+ # `cmd wifi` by convention uses enabled/disabled
+ # instead of enable/disable
+ if [ "x$2" == "xenable" ]; then
+ exec cmd bluetooth_manager enable
+ elif [ "x$2" == "xdisable" ]; then
+ exec cmd bluetooth_manager disable
+ else
+ echo "Control the Bluetooth manager"
+ echo ""
+ echo "usage: svc bluetooth [enable|disable]"
+ echo " Turn Bluetooth on or off."
+ echo ""
+ fi
+ exit 1
+fi
+
export CLASSPATH=/system/framework/svc.jar
exec app_process /system/bin com.android.commands.svc.Svc "$@"
diff --git a/core/api/current.txt b/core/api/current.txt
index 00d7d80808b1..d8ea767a6400 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7388,8 +7388,8 @@ package android.app.admin {
method public int getCurrentFailedPasswordAttempts();
method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
- method @Nullable public String getDeviceManagerRoleHolderPackageName();
method public CharSequence getDeviceOwnerLockScreenInfo();
+ method @Nullable public String getDevicePolicyManagementRoleHolderPackage();
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>);
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>);
method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon);
@@ -10329,7 +10329,6 @@ package android.content {
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
- field @RequiresPermission("android.permission.MANAGE_SENSOR_PRIVACY") public static final String ACTION_VIEW_SAFETY_HUB = "android.intent.action.VIEW_SAFETY_HUB";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -26831,7 +26830,7 @@ package android.net {
field public static final String EXTRA_ERROR_CLASS = "android.net.extra.ERROR_CLASS";
field public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
field public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
- field public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+ field public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS";
field public static final String EXTRA_UNDERLYING_LINK_PROPERTIES = "android.net.extra.UNDERLYING_LINK_PROPERTIES";
field public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
field public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES = "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
@@ -30868,6 +30867,7 @@ package android.os {
field public static final int PREVIEW_SDK_INT;
field public static final String RELEASE;
field @NonNull public static final String RELEASE_OR_CODENAME;
+ field @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY;
field @Deprecated public static final String SDK;
field public static final int SDK_INT;
field public static final String SECURITY_PATCH;
@@ -31576,7 +31576,7 @@ package android.os {
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
- method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>);
+ method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<? extends T>);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Deprecated @Nullable public java.io.Serializable readSerializable();
@@ -31859,7 +31859,7 @@ package android.os {
method public static final boolean is64Bit();
method public static boolean isApplicationUid(int);
method public static final boolean isIsolated();
- method public static final boolean isSupplemental();
+ method public static final boolean isSdkSandbox();
method public static final void killProcess(int);
method public static final int myPid();
method @NonNull public static String myProcessName();
@@ -38081,6 +38081,7 @@ package android.service.autofill {
}
public final class Field {
+ method @Nullable public java.util.regex.Pattern getFilter();
method @Nullable public android.service.autofill.Presentations getPresentations();
method @Nullable public android.view.autofill.AutofillValue getValue();
}
@@ -40754,8 +40755,9 @@ package android.telecom {
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS) public java.util.List<android.telecom.PhoneAccountHandle> getOwnSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
- method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_OWN_CALLS}) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method @Nullable public android.telecom.PhoneAccountHandle getSimCallManagerForSubscription(int);
method @Nullable public String getSystemDialerPackage();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7ec239d681d0..a268f85c6dcd 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -120,7 +120,7 @@ package android.content.pm {
public abstract class PackageManager {
method @NonNull public String getPermissionControllerPackageName();
- method @NonNull public String getSupplementalProcessPackageName();
+ method @NonNull public String getSdkSandboxPackageName();
field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
}
@@ -362,9 +362,9 @@ package android.os {
}
public class Process {
- method public static final boolean isSupplemental(int);
+ method public static final boolean isSdkSandboxUid(int);
method public static final int toAppUid(int);
- method public static final int toSupplementalUid(int);
+ method public static final int toSdkSandboxUid(int);
field public static final int NFC_UID = 1027; // 0x403
field public static final int VPN_UID = 1016; // 0x3f8
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 83464f0ca1df..68ec2df56ea9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -416,7 +416,7 @@ package android {
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultSms = 17039396; // 0x1040024
- field public static final int config_deviceManager;
+ field public static final int config_devicePolicyManagement;
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
@@ -2823,11 +2823,14 @@ package android.companion.virtual {
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities();
- method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method public int getDefaultActivityPolicy();
method public int getLockState();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
@@ -2836,8 +2839,8 @@ package android.companion.virtual {
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
@@ -3035,6 +3038,7 @@ package android.content {
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
field public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public static final String ACTION_VIEW_SAFETY_CENTER_QS = "android.intent.action.VIEW_SAFETY_CENTER_QS";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -5908,16 +5912,16 @@ package android.location {
method @IntRange(from=0, to=1023) public int getIssueOfDataClock();
method @IntRange(from=0, to=255) public int getIssueOfDataEphemeris();
method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef();
- method @IntRange(from=0) public long getTimeOfClock();
- method @IntRange(from=0) public long getTimeOfEphemeris();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method @IntRange(from=0) public long getTimeOfEphemerisSeconds();
method @FloatRange public double getTropoDelayMeters();
method @Nullable public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
method public boolean hasIono();
method public boolean hasIssueOfDataClock();
method public boolean hasIssueOfDataEphemeris();
method public boolean hasPositionVelocityClockInfo();
- method public boolean hasTimeOfClock();
- method public boolean hasTimeOfEphemeris();
+ method public boolean hasTimeOfClockSeconds();
+ method public boolean hasTimeOfEphemerisSeconds();
method public boolean hasTropo();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.SatellitePvt> CREATOR;
@@ -5936,8 +5940,8 @@ package android.location {
method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataClock(@IntRange(from=0, to=1023) int);
method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=255) int);
method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef);
- method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0) long);
- method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0) int);
+ method @NonNull public android.location.SatellitePvt.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemerisSeconds(@IntRange(from=0) long);
method @NonNull public android.location.SatellitePvt.Builder setTropoDelayMeters(@FloatRange(from=0.0f, to=100.0f) double);
method @NonNull public android.location.SatellitePvt.Builder setVelocityEcef(@NonNull android.location.SatellitePvt.VelocityEcef);
}
@@ -6104,7 +6108,7 @@ package android.media {
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addAssistantServicesUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addAssistantServicesUids(@NonNull int[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
@@ -6112,9 +6116,9 @@ package android.media {
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<java.lang.Integer> getActiveAssistantServicesUids();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getActiveAssistantServicesUids();
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<java.lang.Integer> getAssistantServicesUids();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getAssistantServicesUids();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -6139,7 +6143,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull int[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
@@ -6147,7 +6151,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
@@ -8528,14 +8532,21 @@ package android.net {
}
public final class EthernetNetworkUpdateRequest implements android.os.Parcelable {
- ctor public EthernetNetworkUpdateRequest(@NonNull android.net.StaticIpConfiguration, @NonNull android.net.NetworkCapabilities);
method public int describeContents();
- method @NonNull public android.net.StaticIpConfiguration getIpConfig();
+ method @NonNull public android.net.IpConfiguration getIpConfiguration();
method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR;
}
+ public static final class EthernetNetworkUpdateRequest.Builder {
+ ctor public EthernetNetworkUpdateRequest.Builder();
+ ctor public EthernetNetworkUpdateRequest.Builder(@NonNull android.net.EthernetNetworkUpdateRequest);
+ method @NonNull public android.net.EthernetNetworkUpdateRequest build();
+ method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setIpConfiguration(@NonNull android.net.IpConfiguration);
+ method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+ }
+
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public MatchAllNetworkSpecifier();
method public int describeContents();
@@ -10318,6 +10329,7 @@ package android.provider {
field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SCHEDULER = "scheduler";
+ field public static final String NAMESPACE_SDK_SANDBOX = "sdk_sandbox";
field public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
field public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
@@ -15689,6 +15701,11 @@ package android.util {
package android.view {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+ method @NonNull public final java.util.List<android.graphics.Rect> getUnrestrictedPreferKeepClearRects();
+ method @RequiresPermission(android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS) public final void setUnrestrictedPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
+ }
+
public abstract class Window {
method public void addSystemFlags(@android.view.WindowManager.LayoutParams.SystemFlags int);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b752c4de22c7..ed18a2219915 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -155,8 +155,10 @@ package android.app {
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
+ method public boolean isEligibleForLegacyPermissionPrompt();
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+ method public void setEligibleForLegacyPermissionPrompt(boolean);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchWindowingMode(int);
@@ -826,9 +828,9 @@ package android.content.pm {
method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags, int);
method @Nullable public abstract String[] getNamesForUids(int[]);
method @NonNull public String getPermissionControllerPackageName();
+ method @NonNull public String getSdkSandboxPackageName();
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
- method @NonNull public String getSupplementalProcessPackageName();
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method public void holdLock(android.os.IBinder, int);
@@ -1073,7 +1075,7 @@ package android.hardware {
field public static final int DIALOG = 3; // 0x3
field public static final int OTHER = 5; // 0x5
field public static final int QS_TILE = 1; // 0x1
- field public static final int SAFETY_HUB = 6; // 0x6
+ field public static final int SAFETY_CENTER = 6; // 0x6
field public static final int SETTINGS = 2; // 0x2
field public static final int SHELL = 4; // 0x4
}
@@ -1774,7 +1776,7 @@ package android.os {
public class Process {
method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
- method public static final int toSupplementalUid(int);
+ method public static final int toSdkSandboxUid(int);
field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 5ddaa80b1a82..1d14307e6294 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -174,6 +174,13 @@ public class ActivityOptions extends ComponentOptions {
public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";
/**
+ * Indicates that this activity launch is eligible to show a legacy permission prompt
+ * @hide
+ */
+ public static final String KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE =
+ "android:activity.legacyPermissionPromptEligible";
+
+ /**
* Callback for when the last frame of the animation is played.
* @hide
*/
@@ -445,6 +452,7 @@ public class ActivityOptions extends ComponentOptions {
private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
+ private boolean mIsEligibleForLegacyPermissionPrompt;
private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble;
private boolean mTransientLaunch;
@@ -1243,6 +1251,8 @@ public class ActivityOptions extends ComponentOptions {
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
+ mIsEligibleForLegacyPermissionPrompt =
+ opts.getBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE);
}
/**
@@ -1474,6 +1484,24 @@ public class ActivityOptions extends ComponentOptions {
}
/**
+ * Whether the activity is eligible to show a legacy permission prompt
+ * @hide
+ */
+ @TestApi
+ public boolean isEligibleForLegacyPermissionPrompt() {
+ return mIsEligibleForLegacyPermissionPrompt;
+ }
+
+ /**
+ * Sets whether the activity is eligible to show a legacy permission prompt
+ * @hide
+ */
+ @TestApi
+ public void setEligibleForLegacyPermissionPrompt(boolean eligible) {
+ mIsEligibleForLegacyPermissionPrompt = eligible;
+ }
+
+ /**
* Sets whether the activity is to be launched into LockTask mode.
*
* Use this option to start an activity in LockTask mode. Note that only apps permitted by
@@ -1909,6 +1937,7 @@ public class ActivityOptions extends ComponentOptions {
mSpecsFuture = otherOptions.mSpecsFuture;
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
+ mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt;
}
/**
@@ -2084,6 +2113,10 @@ public class ActivityOptions extends ComponentOptions {
if (mLaunchIntoPipParams != null) {
b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
}
+ if (mIsEligibleForLegacyPermissionPrompt) {
+ b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE,
+ mIsEligibleForLegacyPermissionPrompt);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 61d186579f1b..64f0301fab22 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4469,12 +4469,6 @@ public final class ActivityThread extends ClientTransactionHandler
// we are back active so skip it.
unscheduleGcIdler();
- // To investigate "duplciate Application objects" bug (b/185177290)
- if (UserHandle.myUserId() != UserHandle.getUserId(data.info.applicationInfo.uid)) {
- Slog.wtf(TAG, "handleCreateService called with wrong appinfo UID: myUserId="
- + UserHandle.myUserId() + " appinfo.uid=" + data.info.applicationInfo.uid);
- }
-
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 20ffa254a9bf..dca5c542af17 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -165,50 +165,35 @@ public class ApplicationPackageManager extends PackageManager {
public static final String PERMISSION_CONTROLLER_RESOURCE_PACKAGE =
"com.android.permissioncontroller";
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private UserManager mUserManager;
- @GuardedBy("mLock")
- private PermissionManager mPermissionManager;
- @GuardedBy("mLock")
- private PackageInstaller mInstaller;
- @GuardedBy("mLock")
- private ArtManager mArtManager;
- @GuardedBy("mLock")
- private DevicePolicyManager mDevicePolicyManager;
+ private volatile UserManager mUserManager;
+ private volatile PermissionManager mPermissionManager;
+ private volatile PackageInstaller mInstaller;
+ private volatile ArtManager mArtManager;
+ private volatile DevicePolicyManager mDevicePolicyManager;
+ private volatile String mPermissionsControllerPackageName;
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
- @GuardedBy("mLock")
- private String mPermissionsControllerPackageName;
-
UserManager getUserManager() {
- synchronized (mLock) {
- if (mUserManager == null) {
- mUserManager = UserManager.get(mContext);
- }
- return mUserManager;
+ if (mUserManager == null) {
+ mUserManager = UserManager.get(mContext);
}
+ return mUserManager;
}
DevicePolicyManager getDevicePolicyManager() {
- synchronized (mLock) {
- if (mDevicePolicyManager == null) {
- mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
- }
- return mDevicePolicyManager;
+ if (mDevicePolicyManager == null) {
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
}
+ return mDevicePolicyManager;
}
private PermissionManager getPermissionManager() {
- synchronized (mLock) {
- if (mPermissionManager == null) {
- mPermissionManager = mContext.getSystemService(PermissionManager.class);
- }
- return mPermissionManager;
+ if (mPermissionManager == null) {
+ mPermissionManager = mContext.getSystemService(PermissionManager.class);
}
+ return mPermissionManager;
}
@Override
@@ -851,25 +836,23 @@ public class ApplicationPackageManager extends PackageManager {
*/
@Override
public String getPermissionControllerPackageName() {
- synchronized (mLock) {
- if (mPermissionsControllerPackageName == null) {
- try {
- mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ if (mPermissionsControllerPackageName == null) {
+ try {
+ mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return mPermissionsControllerPackageName;
}
+ return mPermissionsControllerPackageName;
}
/**
* @hide
*/
@Override
- public String getSupplementalProcessPackageName() {
+ public String getSdkSandboxPackageName() {
try {
- return mPM.getSupplementalProcessPackageName();
+ return mPM.getSdkSandboxPackageName();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3235,17 +3218,15 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public PackageInstaller getPackageInstaller() {
- synchronized (mLock) {
- if (mInstaller == null) {
- try {
- mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
- mContext.getPackageName(), mContext.getAttributionTag(), getUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ if (mInstaller == null) {
+ try {
+ mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
+ mContext.getPackageName(), mContext.getAttributionTag(), getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return mInstaller;
}
+ return mInstaller;
}
@Override
@@ -3583,16 +3564,14 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public ArtManager getArtManager() {
- synchronized (mLock) {
- if (mArtManager == null) {
- try {
- mArtManager = new ArtManager(mContext, mPM.getArtManager());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ if (mArtManager == null) {
+ try {
+ mArtManager = new ArtManager(mContext, mPM.getArtManager());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return mArtManager;
}
+ return mArtManager;
}
@Override
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 87ba197d8052..cedf483eb076 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -184,8 +184,17 @@ public class KeyguardManager {
})
@interface LockTypes {}
- // TODO(b/220379118): register only one binder listener and keep a map of listener to executor.
- private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener>
+ private final IKeyguardLockedStateListener mIKeyguardLockedStateListener =
+ new IKeyguardLockedStateListener.Stub() {
+ @Override
+ public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+ mKeyguardLockedStateListeners.forEach((listener, executor) -> {
+ executor.execute(
+ () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked));
+ });
+ }
+ };
+ private final ArrayMap<KeyguardLockedStateListener, Executor>
mKeyguardLockedStateListeners = new ArrayMap<>();
/**
@@ -1102,17 +1111,12 @@ public class KeyguardManager {
public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor,
@NonNull KeyguardLockedStateListener listener) {
synchronized (mKeyguardLockedStateListeners) {
+ mKeyguardLockedStateListeners.put(listener, executor);
+ if (mKeyguardLockedStateListeners.size() > 1) {
+ return;
+ }
try {
- final IKeyguardLockedStateListener innerListener =
- new IKeyguardLockedStateListener.Stub() {
- @Override
- public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
- executor.execute(
- () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked));
- }
- };
- mWM.addKeyguardLockedStateListener(innerListener);
- mKeyguardLockedStateListeners.put(listener, innerListener);
+ mWM.addKeyguardLockedStateListener(mIKeyguardLockedStateListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1125,17 +1129,15 @@ public class KeyguardManager {
@RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) {
synchronized (mKeyguardLockedStateListeners) {
- IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get(
- listener);
- if (innerListener == null) {
+ mKeyguardLockedStateListeners.remove(listener);
+ if (!mKeyguardLockedStateListeners.isEmpty()) {
return;
}
try {
- mWM.removeKeyguardLockedStateListener(innerListener);
+ mWM.removeKeyguardLockedStateListener(mIKeyguardLockedStateListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- mKeyguardLockedStateListeners.remove(listener);
}
}
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 99a523a0e18a..cf259e577a5a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -160,6 +160,13 @@ public final class LoadedApk {
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
private AppComponentFactory mAppComponentFactory;
+
+ /**
+ * We cache the instantiated application object for each package on this process here.
+ */
+ @GuardedBy("sApplications")
+ private static final ArrayMap<String, Application> sApplications = new ArrayMap<>(4);
+
private final Object mLock = new Object();
Application getApplication() {
@@ -1345,14 +1352,6 @@ public final class LoadedApk {
return mResources;
}
- /**
- * Used to investigate "duplicate app objects" bug (b/185177290).
- * makeApplication() should only be called on the main thread, so no synchronization should
- * be needed, but syncing anyway just in case.
- */
- @GuardedBy("sApplicationCache")
- private static final ArrayMap<String, Application> sApplicationCache = new ArrayMap<>(4);
-
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
@@ -1361,15 +1360,8 @@ public final class LoadedApk {
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
- // For b/185177290.
- final boolean wrongUser =
- UserHandle.myUserId() != UserHandle.getUserId(mApplicationInfo.uid);
- if (wrongUser) {
- Slog.wtf(TAG, "makeApplication called with wrong appinfo UID: myUserId="
- + UserHandle.myUserId() + " appinfo.uid=" + mApplicationInfo.uid);
- }
- synchronized (sApplicationCache) {
- final Application cached = sApplicationCache.get(mPackageName);
+ synchronized (sApplications) {
+ final Application cached = sApplications.get(mPackageName);
if (cached != null) {
// Looks like this is always happening for the system server, because
// the LoadedApk created in systemMain() -> attach() isn't cached properly?
@@ -1377,8 +1369,8 @@ public final class LoadedApk {
Slog.wtf(TAG, "App instance already created for package=" + mPackageName
+ " instance=" + cached);
}
- // TODO Return the cached one, unles it's for the wrong user?
- // For now, we just add WTF checks.
+ mApplication = cached;
+ return cached;
}
}
@@ -1429,8 +1421,8 @@ public final class LoadedApk {
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
- synchronized (sApplicationCache) {
- sApplicationCache.put(mPackageName, app);
+ synchronized (sApplications) {
+ sApplications.put(mPackageName, app);
}
if (instrumentation != null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9dd206e21c44..7e0cea8921a4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9009,6 +9009,10 @@ public class Notification implements Parcelable
* information that will replace the default values for the output switcher chip on the
* media control, as well as an intent to use when the output switcher chip is tapped,
* on devices where this is supported.
+ * <p>
+ * This method is intended for system applications to provide information and/or
+ * functionality that would otherwise be unavailable to the default output switcher because
+ * the media originated on a remote device.
*
* @param deviceName The name of the remote device to display
* @param iconResource Icon resource representing the device
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index eeb4705fc7d3..58db93c123bb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -18,6 +18,7 @@ package android.app;
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
+import android.adservices.AdServicesFrameworkInitializer;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -36,6 +37,7 @@ import android.app.job.JobSchedulerFrameworkInitializer;
import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionManager;
import android.app.role.RoleFrameworkInitializer;
+import android.app.sdksandbox.SdkSandboxManagerFrameworkInitializer;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
import android.app.smartspace.SmartspaceManager;
@@ -208,7 +210,6 @@ import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
import android.service.vr.IVrManager;
-import android.supplementalprocess.SupplementalProcessFrameworkInitializer;
import android.telecom.TelecomManager;
import android.telephony.MmsManager;
import android.telephony.TelephonyFrameworkInitializer;
@@ -1564,7 +1565,8 @@ public final class SystemServiceRegistry {
MediaFrameworkInitializer.registerServiceWrappers();
RoleFrameworkInitializer.registerServiceWrappers();
SchedulingFrameworkInitializer.registerServiceWrappers();
- SupplementalProcessFrameworkInitializer.registerServiceWrappers();
+ SdkSandboxManagerFrameworkInitializer.registerServiceWrappers();
+ AdServicesFrameworkInitializer.registerServiceWrappers();
UwbFrameworkInitializer.registerServiceWrappers();
SafetyCenterFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index b791f05c1076..5c1ab3879487 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -841,15 +841,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
/**
- * Returns true if the windowingMode represents a split window.
- * @hide
- */
- public static boolean isSplitScreenWindowingMode(int windowingMode) {
- return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- /**
* Returns true if the windows associated with this window configuration can receive input keys.
* @hide
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b944468f10b1..d5d14c6c0fbb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -545,15 +545,16 @@ public class DevicePolicyManager {
= "android.app.action.PROVISION_FINALIZATION";
/**
- * Activity action: starts the managed profile provisioning flow inside the device management
- * role holder.
+ * Activity action: starts the managed profile provisioning flow inside the device policy
+ * management role holder.
*
* <p>During the managed profile provisioning flow, the platform-provided provisioning handler
- * will delegate provisioning to the device management role holder, by firing this intent.
- * Third-party mobile device management applications attempting to fire this intent will
+ * will delegate provisioning to the device policy management role holder, by firing this
+ * intent. Third-party mobile device management applications attempting to fire this intent will
* receive a {@link SecurityException}.
*
- * <p>Device management role holders are required to have a handler for this intent action.
+ * <p>Device policy management role holders are required to have a handler for this intent
+ * action.
*
* <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
* of the role holder to restore its state from this extra. This is the same {@link Bundle}
@@ -596,15 +597,16 @@ public class DevicePolicyManager {
public static final int RESULT_DEVICE_OWNER_SET = 123;
/**
- * Activity action: starts the trusted source provisioning flow inside the device management
- * role holder.
+ * Activity action: starts the trusted source provisioning flow inside the device policy
+ * management role holder.
*
* <p>During the trusted source provisioning flow, the platform-provided provisioning handler
- * will delegate provisioning to the device management role holder, by firing this intent.
- * Third-party mobile device management applications attempting to fire this intent will
+ * will delegate provisioning to the device policy management role holder, by firing this
+ * intent. Third-party mobile device management applications attempting to fire this intent will
* receive a {@link SecurityException}.
*
- * <p>Device management role holders are required to have a handler for this intent action.
+ * <p>Device policy management role holders are required to have a handler for this intent
+ * action.
*
* <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
* of the role holder to restore its state from this extra. This is the same {@link Bundle}
@@ -624,15 +626,16 @@ public class DevicePolicyManager {
"android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
/**
- * Activity action: starts the provisioning finalization flow inside the device management
- * role holder.
+ * Activity action: starts the provisioning finalization flow inside the device policy
+ * management role holder.
*
* <p>During the provisioning finalization flow, the platform-provided provisioning handler
- * will delegate provisioning to the device management role holder, by firing this intent.
- * Third-party mobile device management applications attempting to fire this intent will
+ * will delegate provisioning to the device policy management role holder, by firing this
+ * intent. Third-party mobile device management applications attempting to fire this intent will
* receive a {@link SecurityException}.
*
- * <p>Device management role holders are required to have a handler for this intent action.
+ * <p>Device policy management role holders are required to have a handler for this intent
+ * action.
*
* <p>This handler forwards the result from the admin app's {@link
* #ACTION_ADMIN_POLICY_COMPLIANCE} handler. Result code {@link Activity#RESULT_CANCELED}
@@ -697,9 +700,9 @@ public class DevicePolicyManager {
* A boolean extra indicating whether offline provisioning is allowed.
*
* <p>For the online provisioning flow, there will be an attempt to download and install
- * the latest version of the device management role holder. The platform will then delegate
- * provisioning to the device management role holder via role holder-specific provisioning
- * actions.
+ * the latest version of the device policy management role holder. The platform will then
+ * delegate provisioning to the device policy management role holder via role holder-specific
+ * provisioning actions.
*
* <p>For the offline provisioning flow, the provisioning flow will always be handled by
* the platform.
@@ -720,8 +723,8 @@ public class DevicePolicyManager {
"android.app.extra.PROVISIONING_ALLOW_OFFLINE";
/**
- * A String extra holding a url that specifies the download location of the device manager
- * role holder package.
+ * A String extra holding a url that specifies the download location of the device policy
+ * management role holder package.
*
* <p>This is only meant to be used in cases when a specific variant of the role holder package
* is needed (such as a debug variant). If not provided, the default variant of the device
@@ -777,13 +780,13 @@ public class DevicePolicyManager {
/**
* An extra of type {@link android.os.PersistableBundle} that allows the provisioning initiator
- * to pass data to the device manager role holder.
+ * to pass data to the device policy management role holder.
*
- * <p>The device manager role holder will receive this extra via the {@link
+ * <p>The device policy management role holder will receive this extra via the {@link
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent.
*
* <p>The contents of this extra are up to the contract between the provisioning initiator
- * and the device manager role holder.
+ * and the device policy management role holder.
*
* <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
* or in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
@@ -814,18 +817,19 @@ public class DevicePolicyManager {
* <p>If {@link #EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT} is set to {@code false},
* this result will be supplied as part of the result {@link Intent} for provisioning actions
* such as {@link #ACTION_PROVISION_MANAGED_PROFILE}. This result will also be supplied as
- * part of the result {@link Intent} for the device manager role holder provisioning actions.
+ * part of the result {@link Intent} for the device policy management role holder provisioning
+ * actions.
*/
public static final String EXTRA_RESULT_LAUNCH_INTENT =
"android.app.extra.RESULT_LAUNCH_INTENT";
/**
* A boolean extra that determines whether the provisioning flow should launch the resulting
- * launch intent, if one is supplied by the device manager role holder via {@link
+ * launch intent, if one is supplied by the device policy management role holder via {@link
* #EXTRA_RESULT_LAUNCH_INTENT}. Default value is {@code false}.
*
* <p>If {@code true}, the resulting intent will be launched by the provisioning flow, if one
- * is supplied by the device manager role holder.
+ * is supplied by the device policy management role holder.
*
* <p>If {@code false}, the resulting intent will be returned as {@link
* #EXTRA_RESULT_LAUNCH_INTENT} to the provisioning initiator, if one is supplied by the device
@@ -3220,10 +3224,10 @@ public class DevicePolicyManager {
"android.app.action.ADMIN_POLICY_COMPLIANCE";
/**
- * Activity action: Starts the device management role holder updater.
+ * Activity action: Starts the device policy management role holder updater.
*
- * <p>The activity must handle the device management role holder update and set the intent
- * result to either {@link Activity#RESULT_OK} if the update was successful, {@link
+ * <p>The activity must handle the device policy management role holder update and set the
+ * intent result to either {@link Activity#RESULT_OK} if the update was successful, {@link
* #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if it encounters a problem
* that may be solved by relaunching it again, or {@link
* #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters a problem
@@ -3261,9 +3265,9 @@ public class DevicePolicyManager {
/**
* An {@link Intent} extra which resolves to a custom user consent screen.
*
- * <p>If this extra is provided to the device management role holder via either {@link
+ * <p>If this extra is provided to the device policy management role holder via either {@link
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link
- * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}, the device management role holder must
+ * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}, the device policy management role holder must
* launch this intent which shows the custom user consent screen, replacing its own standard
* consent screen.
*
@@ -3279,9 +3283,9 @@ public class DevicePolicyManager {
* </ul>
*
* <p>If the custom consent screens are granted by the user {@link Activity#RESULT_OK} is
- * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device management
- * role holder should ensure that the provisioning flow terminates immediately if consent
- * is not granted by the user.
+ * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device policy
+ * management role holder should ensure that the provisioning flow terminates immediately if
+ * consent is not granted by the user.
*
* @hide
*/
@@ -15647,14 +15651,15 @@ public class DevicePolicyManager {
}
/**
- * Returns the package name of the device manager role holder.
+ * Returns the package name of the device policy management role holder.
*
- * <p>If the device manager role holder is not configured for this device, returns {@code null}.
+ * <p>If the device policy management role holder is not configured for this device, returns
+ * {@code null}.
*/
@Nullable
- public String getDeviceManagerRoleHolderPackageName() {
- String deviceManagerConfig =
- mContext.getString(com.android.internal.R.string.config_deviceManager);
+ public String getDevicePolicyManagementRoleHolderPackage() {
+ String deviceManagerConfig = mContext.getString(
+ com.android.internal.R.string.config_devicePolicyManagement);
return extractPackageNameFromDeviceManagerConfig(deviceManagerConfig);
}
diff --git a/core/java/android/app/admin/WifiSsidPolicy.aidl b/core/java/android/app/admin/WifiSsidPolicy.aidl
new file mode 100644
index 000000000000..150705ddce62
--- /dev/null
+++ b/core/java/android/app/admin/WifiSsidPolicy.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable WifiSsidPolicy; \ No newline at end of file
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 45d0ad51da04..41b1a1feae80 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -20,9 +20,7 @@ import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
@@ -64,20 +62,43 @@ public final class VirtualDeviceParams implements Parcelable {
*/
public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
+ /** @hide */
+ @IntDef(prefix = "ACTIVITY_POLICY_",
+ value = {ACTIVITY_POLICY_DEFAULT_ALLOWED, ACTIVITY_POLICY_DEFAULT_BLOCKED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface ActivityPolicy {}
+
+ /**
+ * Indicates that activities are allowed by default on this virtual device, unless they are
+ * explicitly blocked by {@link Builder#setBlockedActivities}.
+ */
+ public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0;
+
+ /**
+ * Indicates that activities are blocked by default on this virtual device, unless they are
+ * allowed by {@link Builder#setAllowedActivities}.
+ */
+ public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1;
+
private final int mLockState;
private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
- @Nullable private final ArraySet<ComponentName> mAllowedActivities;
- @Nullable private final ArraySet<ComponentName> mBlockedActivities;
+ @NonNull private final ArraySet<ComponentName> mAllowedActivities;
+ @NonNull private final ArraySet<ComponentName> mBlockedActivities;
+ @ActivityPolicy
+ private final int mDefaultActivityPolicy;
private VirtualDeviceParams(
@LockState int lockState,
@NonNull Set<UserHandle> usersWithMatchingAccounts,
- @Nullable Set<ComponentName> allowedActivities,
- @Nullable Set<ComponentName> blockedActivities) {
+ @NonNull Set<ComponentName> allowedActivities,
+ @NonNull Set<ComponentName> blockedActivities,
+ @ActivityPolicy int defaultActivityPolicy) {
mLockState = lockState;
mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+ mDefaultActivityPolicy = defaultActivityPolicy;
}
@SuppressWarnings("unchecked")
@@ -86,6 +107,7 @@ public final class VirtualDeviceParams implements Parcelable {
mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
+ mDefaultActivityPolicy = parcel.readInt();
}
/**
@@ -113,12 +135,10 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see Builder#setAllowedActivities(Set)
*/
- // Null and empty have different semantics - Null allows all activities to be streamed
- @SuppressLint("NullableCollection")
- @Nullable
+ @NonNull
public Set<ComponentName> getAllowedActivities() {
if (mAllowedActivities == null) {
- return null;
+ return Collections.emptySet();
}
return Collections.unmodifiableSet(mAllowedActivities);
}
@@ -129,16 +149,27 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see Builder#setBlockedActivities(Set)
*/
- // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
- @SuppressLint("NullableCollection")
- @Nullable
+ @NonNull
public Set<ComponentName> getBlockedActivities() {
if (mBlockedActivities == null) {
- return null;
+ return Collections.emptySet();
}
return Collections.unmodifiableSet(mBlockedActivities);
}
+ /**
+ * Returns {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED} if activities are allowed to launch on this
+ * virtual device by default, or {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED} if activities must be
+ * allowed by {@link Builder#setAllowedActivities} to launch here.
+ *
+ * @see Builder#setBlockedActivities
+ * @see Builder#setAllowedActivities
+ */
+ @ActivityPolicy
+ public int getDefaultActivityPolicy() {
+ return mDefaultActivityPolicy;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -150,6 +181,7 @@ public final class VirtualDeviceParams implements Parcelable {
dest.writeArraySet(mUsersWithMatchingAccounts);
dest.writeArraySet(mAllowedActivities);
dest.writeArraySet(mBlockedActivities);
+ dest.writeInt(mDefaultActivityPolicy);
}
@Override
@@ -164,12 +196,15 @@ public final class VirtualDeviceParams implements Parcelable {
return mLockState == that.mLockState
&& mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
&& Objects.equals(mAllowedActivities, that.mAllowedActivities)
- && Objects.equals(mBlockedActivities, that.mBlockedActivities);
+ && Objects.equals(mBlockedActivities, that.mBlockedActivities)
+ && mDefaultActivityPolicy == that.mDefaultActivityPolicy;
}
@Override
public int hashCode() {
- return Objects.hash(mLockState, mUsersWithMatchingAccounts);
+ return Objects.hash(
+ mLockState, mUsersWithMatchingAccounts, mAllowedActivities, mBlockedActivities,
+ mDefaultActivityPolicy);
}
@Override
@@ -180,6 +215,7 @@ public final class VirtualDeviceParams implements Parcelable {
+ " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+ " mAllowedActivities=" + mAllowedActivities
+ " mBlockedActivities=" + mBlockedActivities
+ + " mDefaultActivityPolicy=" + mDefaultActivityPolicy
+ ")";
}
@@ -202,8 +238,11 @@ public final class VirtualDeviceParams implements Parcelable {
private @LockState int mLockState = LOCK_STATE_DEFAULT;
private Set<UserHandle> mUsersWithMatchingAccounts;
- @Nullable private Set<ComponentName> mBlockedActivities;
- @Nullable private Set<ComponentName> mAllowedActivities;
+ @NonNull private Set<ComponentName> mBlockedActivities = Collections.emptySet();
+ @NonNull private Set<ComponentName> mAllowedActivities = Collections.emptySet();
+ @ActivityPolicy
+ private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ private boolean mDefaultActivityPolicyConfigured = false;
/**
* Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -248,53 +287,53 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
- * Sets the activities allowed to be launched in the virtual device. If
- * {@code allowedActivities} is non-null, all activities other than the ones in the set will
- * be blocked from launching.
+ * Sets the activities allowed to be launched in the virtual device. Calling this method
+ * will cause {@link #getDefaultActivityPolicy()} to be
+ * {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED}, meaning activities not in
+ * {@code allowedActivities} will be blocked from launching here.
*
- * <p>{@code allowedActivities} and the set in {@link #setBlockedActivities(Set)} cannot
- * both be non-null at the same time.
+ * <p>This method must not be called if {@link #setBlockedActivities(Set)} has been called.
*
- * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been set to a
- * non-null value.
+ * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been called.
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
*/
- // Null and empty have different semantics - Null allows all activities to be streamed
- @SuppressLint("NullableCollection")
@NonNull
- public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
- if (mBlockedActivities != null && allowedActivities != null) {
+ public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) {
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_BLOCKED) {
throw new IllegalArgumentException(
"Allowed activities and Blocked activities cannot both be set.");
}
+ mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_BLOCKED;
+ mDefaultActivityPolicyConfigured = true;
mAllowedActivities = allowedActivities;
return this;
}
/**
- * Sets the activities blocked from launching in the virtual device. If the {@code
- * blockedActivities} is non-null, activities in the set are blocked from launching in the
- * virtual device.
+ * Sets the activities blocked from launching in the virtual device. Calling this method
+ * will cause {@link #getDefaultActivityPolicy()} to be
+ * {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch here
+ * unless they are in {@code blockedActivities}.
*
- * {@code blockedActivities} and the set in {@link #setAllowedActivities(Set)} cannot both
- * be non-null at the same time.
+ * <p>This method must not be called if {@link #setAllowedActivities(Set)} has been called.
*
- * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been set to a
- * non-null value.
+ * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been called.
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
*/
- // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
- @SuppressLint("NullableCollection")
@NonNull
- public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
- if (mAllowedActivities != null && blockedActivities != null) {
+ public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) {
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_ALLOWED) {
throw new IllegalArgumentException(
"Allowed activities and Blocked activities cannot both be set.");
}
+ mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ mDefaultActivityPolicyConfigured = true;
mBlockedActivities = blockedActivities;
return this;
}
@@ -307,13 +346,12 @@ public final class VirtualDeviceParams implements Parcelable {
if (mUsersWithMatchingAccounts == null) {
mUsersWithMatchingAccounts = Collections.emptySet();
}
- if (mAllowedActivities != null && mBlockedActivities != null) {
- // Should never reach here because the setters block this as well.
- throw new IllegalStateException(
- "Allowed activities and Blocked activities cannot both be set.");
- }
- return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts,
- mAllowedActivities, mBlockedActivities);
+ return new VirtualDeviceParams(
+ mLockState,
+ mUsersWithMatchingAccounts,
+ mAllowedActivities,
+ mBlockedActivities,
+ mDefaultActivityPolicy);
}
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0aa25ef97dd0..478befd9c26d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2054,7 +2054,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
/**
- * Activity action: Launch the Safety Hub UI.
+ * Activity action: Launch the Safety Center Quick Settings UI.
*
* <p>
* Input: Nothing.
@@ -2062,11 +2062,14 @@ public class Intent implements Parcelable, Cloneable {
* <p>
* Output: Nothing.
* </p>
+ *
+ * @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public static final String ACTION_VIEW_SAFETY_HUB =
- "android.intent.action.VIEW_SAFETY_HUB";
+ public static final String ACTION_VIEW_SAFETY_CENTER_QS =
+ "android.intent.action.VIEW_SAFETY_CENTER_QS";
/**
* Activity action: Launch UI to manage a default app.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 30aed8bc4de7..e9e6cd3a54c5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -653,7 +653,7 @@ interface IPackageManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
String getPermissionControllerPackageName();
- String getSupplementalProcessPackageName();
+ String getSdkSandboxPackageName();
ParceledListSlice getInstantApps(int userId);
byte[] getInstantAppCookie(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a162c41e095e..f4bc1616da2b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5772,16 +5772,16 @@ public abstract class PackageManager {
}
/**
- * Returns the package name of the component implementing supplemental process service.
+ * Returns the package name of the component implementing sdk sandbox service.
*
- * @return the package name of the component implementing supplemental process service
+ * @return the package name of the component implementing sdk sandbox service
*
* @hide
*/
@NonNull
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
- public String getSupplementalProcessPackageName() {
+ public String getSdkSandboxPackageName() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ebef0535f077..a03286d3ec6f 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2674,9 +2674,8 @@ public class Resources {
// Putting into a map keyed on the apk assets to deduplicate resources that are different
// objects but ultimately represent the same assets
Map<List<ApkAssets>, Resources> history = new ArrayMap<>();
- for (Resources r : sResourcesHistory) {
- history.put(Arrays.asList(r.mResourcesImpl.mAssets.getApkAssets()), r);
- }
+ sResourcesHistory.forEach(
+ r -> history.put(Arrays.asList(r.mResourcesImpl.mAssets.getApkAssets()), r));
int i = 0;
for (Resources r : history.values()) {
if (r != null) {
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index a3cc01c0adfb..0460e5831e33 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -136,9 +136,9 @@ public final class SensorPrivacyManager {
public static final int OTHER = SensorPrivacyToggleSourceProto.OTHER;
/**
- * Constant for SAFETY_HUB.
+ * Constant for SAFETY_CENTER.
*/
- public static final int SAFETY_HUB = SensorPrivacyToggleSourceProto.SAFETY_HUB;
+ public static final int SAFETY_CENTER = SensorPrivacyToggleSourceProto.SAFETY_CENTER;
/**
* Source for toggling sensors
@@ -151,7 +151,7 @@ public final class SensorPrivacyManager {
DIALOG,
SHELL,
OTHER,
- SAFETY_HUB
+ SAFETY_CENTER
})
@Retention(RetentionPolicy.SOURCE)
public @interface Source {}
@@ -652,7 +652,7 @@ public final class SensorPrivacyManager {
String packageName = mContext.getOpPackageName();
if (Objects.equals(packageName,
mContext.getPackageManager().getPermissionControllerPackageName())) {
- return Sources.SAFETY_HUB;
+ return Sources.SAFETY_CENTER;
}
return Sources.OTHER;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index ada51559a38d..3f139f02efaf 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -104,16 +104,16 @@ public class BiometricManager {
public static final int BIOMETRIC_MULTI_SENSOR_DEFAULT = 0;
/**
- * Prefer the face sensor and fall back to fingerprint when needed.
+ * Use face and fingerprint sensors together.
* @hide
*/
- public static final int BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT = 1;
+ public static final int BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE = 1;
/**
* @hide
*/
@IntDef({BIOMETRIC_MULTI_SENSOR_DEFAULT,
- BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT})
+ BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE})
@Retention(RetentionPolicy.SOURCE)
public @interface BiometricMultiSensorMode {}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 2c3c8c353e8c..42aad36e44c1 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -63,7 +63,7 @@ interface IBiometricService {
// Notify BiometricService when <Biometric>Service is ready to start the prepared client.
// Client lifecycle is still managed in <Biometric>Service.
- void onReadyForAuthentication(int cookie);
+ void onReadyForAuthentication(long requestId, int cookie);
// Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
// specified user. This happens when enrollments have been added on devices with multiple
diff --git a/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
index 5d9b5f3bcc05..450c5ceab04c 100644
--- a/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricSysuiReceiver.aidl
@@ -30,6 +30,4 @@ oneway interface IBiometricSysuiReceiver {
void onSystemEvent(int event);
// Notifies that the dialog has finished animating.
void onDialogAnimatedIn();
- // For multi-sensor devices, notifies that the fingerprint should start now.
- void onStartFingerprintNow();
}
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 779d931245c8..c51444cd31b6 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -108,7 +108,7 @@ public class VpnManager {
* <ul>
* <li>{@link #EXTRA_SESSION_KEY}, a {@code String} for the session key, as returned by
* {@link #startProvisionedVpnProfileSession}.
- * <li>{@link #EXTRA_TIMESTAMP}, a long for the timestamp at which the error occurred,
+ * <li>{@link #EXTRA_TIMESTAMP_MILLIS}, a long for the timestamp at which the error occurred,
* in milliseconds since the epoch, as returned by
* {@link java.lang.System#currentTimeMillis}.
* <li>{@link #EXTRA_UNDERLYING_NETWORK}, a {@link Network} containing the underlying
@@ -196,7 +196,7 @@ public class VpnManager {
* This is a number of milliseconds since the epoch, suitable to be compared with
* {@link java.lang.System#currentTimeMillis}.
*/
- public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+ public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS";
/**
* Extra for the error class, as an {@code int}.
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index c8b4226ecae0..07fbe4a04ff1 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -17,9 +17,11 @@
package android.os;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ProcessInfo;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.Zygote;
import dalvik.system.VMRuntime;
@@ -45,8 +47,6 @@ public class AppZygote {
// Last UID/GID of the range the AppZygote can setuid()/setgid() to
private final int mZygoteUidGidMax;
- private final int mZygoteRuntimeFlags;
-
private final Object mLock = new Object();
/**
@@ -57,14 +57,15 @@ public class AppZygote {
private ChildZygoteProcess mZygote;
private final ApplicationInfo mAppInfo;
+ private final ProcessInfo mProcessInfo;
- public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax,
- int runtimeFlags) {
+ public AppZygote(ApplicationInfo appInfo, ProcessInfo processInfo, int zygoteUid, int uidGidMin,
+ int uidGidMax) {
mAppInfo = appInfo;
+ mProcessInfo = processInfo;
mZygoteUid = zygoteUid;
mZygoteUidGidMin = uidGidMin;
mZygoteUidGidMax = uidGidMax;
- mZygoteRuntimeFlags = runtimeFlags;
}
/**
@@ -108,13 +109,15 @@ public class AppZygote {
String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
Build.SUPPORTED_ABIS[0];
try {
+ int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
+ mAppInfo, mProcessInfo);
mZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.AppZygoteInit",
mAppInfo.processName + "_zygote",
mZygoteUid,
mZygoteUid,
null, // gids
- mZygoteRuntimeFlags, // runtimeFlags
+ runtimeFlags,
"app_zygote", // seInfo
abi, // abi
abi, // acceptedAbiList
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1d1f17df17df..1c85f692b232 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -285,13 +285,20 @@ public class Build {
public static final String RELEASE = getString("ro.build.version.release");
/**
- * The version string we show to the user; may be {@link #RELEASE} or
- * {@link #CODENAME} if not a final release build.
+ * The version string. May be {@link #RELEASE} or {@link #CODENAME} if
+ * not a final release build.
*/
@NonNull public static final String RELEASE_OR_CODENAME = getString(
"ro.build.version.release_or_codename");
/**
+ * The version string we show to the user; may be {@link #RELEASE} or
+ * a descriptive string if not a final release build.
+ */
+ @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY = getString(
+ "ro.build.version.release_or_preview_display");
+
+ /**
* The base OS build the product is based on.
*/
public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3d129417e53b..79fa4fbe0447 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -29,7 +29,6 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.storage.StorageManager;
@@ -1333,7 +1332,7 @@ public class Environment {
final Context context = AppGlobals.getInitialApplication();
final int uid = context.getApplicationInfo().uid;
// Isolated processes and Instant apps are never allowed to be in scoped storage
- if (Process.isIsolated(uid) || Process.isSupplemental(uid)) {
+ if (Process.isIsolated(uid) || Process.isSdkSandboxUid(uid)) {
return false;
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 5d9f2189df1b..8e5ed8f6e578 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -52,6 +52,9 @@ per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Telephony* = file:/telephony/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
+# Time
+per-file *Clock* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
+
# RecoverySystem
per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 09cfb6e70cba..978e99d6c5c5 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -201,7 +201,7 @@ import java.util.function.Supplier;
* The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
* {@link #readFileDescriptor()}.
*
- * <h3>Untyped Containers</h3>
+ * <h3>Parcelable Containers</h3>
*
* <p>A final class of methods are for writing and reading standard Java
* containers of arbitrary types. These all revolve around the
@@ -213,6 +213,19 @@ import java.util.function.Supplier;
* {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)},
* {@link #writeSparseArray(SparseArray)},
* {@link #readSparseArray(ClassLoader)}.
+ *
+ * <h3>Restricted Parcelable Containers</h3>
+ *
+ * <p>A final class of methods are for reading standard Java containers of restricted types.
+ * These methods replace methods for reading containers of arbitrary types from previous section
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. The pairing writing methods are
+ * still the same from previous section.
+ * These methods accepts additional {@code clazz} parameters as the required types.
+ * The Restricted Parcelable container methods are {@link #readArray(ClassLoader, Class)},
+ * {@link #readList(List, ClassLoader, Class)},
+ * {@link #readArrayList(ClassLoader, Class)},
+ * {@link #readMap(Map, ClassLoader, Class, Class)},
+ * {@link #readSparseArray(ClassLoader, Class)}.
*/
public final class Parcel {
@@ -3839,7 +3852,7 @@ public final class Parcel {
*/
@NonNull
public <T> List<T> readParcelableList(@NonNull List<T> list,
- @Nullable ClassLoader cl, @NonNull Class<T> clazz) {
+ @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
Objects.requireNonNull(list);
Objects.requireNonNull(clazz);
return readParcelableListInternal(list, cl, clazz);
@@ -3850,7 +3863,7 @@ public final class Parcel {
*/
@NonNull
private <T> List<T> readParcelableListInternal(@NonNull List<T> list,
- @Nullable ClassLoader cl, @Nullable Class<T> clazz) {
+ @Nullable ClassLoader cl, @Nullable Class<? extends T> clazz) {
final int n = readInt();
if (n == -1) {
list.clear();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 17b5ec5ca01b..f069158c9242 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -281,23 +281,23 @@ public class Process {
/**
* Defines the start of a range of UIDs going from this number to
- * {@link #LAST_SUPPLEMENTAL_UID} that are reserved for assigning to
- * supplemental processes. There is a 1-1 mapping between a supplemental
+ * {@link #LAST_SDK_SANDBOX_UID} that are reserved for assigning to
+ * sdk sandbox processes. There is a 1-1 mapping between a sdk sandbox
* process UID and the app that it belongs to, which can be computed by
- * subtracting (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID) from the
- * uid of a supplemental process.
+ * subtracting (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID) from the
+ * uid of a sdk sandbox process.
*
* Note that there are no GIDs associated with these processes; storage
* attribution for them will be done using project IDs.
* @hide
*/
- public static final int FIRST_SUPPLEMENTAL_UID = 20000;
+ public static final int FIRST_SDK_SANDBOX_UID = 20000;
/**
- * Last UID that is used for supplemental processes.
+ * Last UID that is used for sdk sandbox processes.
* @hide
*/
- public static final int LAST_SUPPLEMENTAL_UID = 29999;
+ public static final int LAST_SDK_SANDBOX_UID = 29999;
/**
* First uid used for fully isolated sandboxed processes spawned from an app zygote
@@ -901,44 +901,44 @@ public class Process {
}
/**
- * Returns whether the provided UID belongs to a supplemental process.
+ * Returns whether the provided UID belongs to a SDK sandbox process.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
- public static final boolean isSupplemental(int uid) {
+ public static final boolean isSdkSandboxUid(int uid) {
uid = UserHandle.getAppId(uid);
- return (uid >= FIRST_SUPPLEMENTAL_UID && uid <= LAST_SUPPLEMENTAL_UID);
+ return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID);
}
/**
*
- * Returns the app process corresponding to a supplemental process.
+ * Returns the app process corresponding to a sdk sandbox process.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int toAppUid(int uid) {
- return uid - (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+ return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
/**
*
- * Returns the supplemental process corresponding to an app process.
+ * Returns the sdk sandbox process corresponding to an app process.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
- public static final int toSupplementalUid(int uid) {
- return uid + (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+ public static final int toSdkSandboxUid(int uid) {
+ return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
/**
- * Returns whether the current process is a supplemental process.
+ * Returns whether the current process is a sdk sandbox process.
*/
- public static final boolean isSupplemental() {
- return isSupplemental(myUid());
+ public static final boolean isSdkSandbox() {
+ return isSdkSandboxUid(myUid());
}
/**
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 58ca97860737..2d287e96ef60 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -108,7 +108,7 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
Preconditions.checkArgumentInRange(mPrimitiveId, VibrationEffect.Composition.PRIMITIVE_NOOP,
VibrationEffect.Composition.PRIMITIVE_LOW_TICK, "primitiveId");
Preconditions.checkArgumentInRange(mScale, 0f, 1f, "scale");
- Preconditions.checkArgumentNonnegative(mDelay, "primitive delay should be >= 0");
+ VibrationEffectSegment.checkDurationArgument(mDelay, "delay");
}
@Override
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index 9e1f6360b090..d7d8c49fe28c 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -108,14 +108,9 @@ public final class RampSegment extends VibrationEffectSegment {
/** @hide */
@Override
public void validate() {
- Preconditions.checkArgumentNonNegative(mStartFrequencyHz,
- "Frequencies must all be >= 0, got start frequency of " + mStartFrequencyHz);
- Preconditions.checkArgumentFinite(mStartFrequencyHz, "startFrequencyHz");
- Preconditions.checkArgumentNonNegative(mEndFrequencyHz,
- "Frequencies must all be >= 0, got end frequency of " + mEndFrequencyHz);
- Preconditions.checkArgumentFinite(mEndFrequencyHz, "endFrequencyHz");
- Preconditions.checkArgumentNonnegative(mDuration,
- "Durations must all be >= 0, got " + mDuration);
+ VibrationEffectSegment.checkFrequencyArgument(mStartFrequencyHz, "startFrequencyHz");
+ VibrationEffectSegment.checkFrequencyArgument(mEndFrequencyHz, "endFrequencyHz");
+ VibrationEffectSegment.checkDurationArgument(mDuration, "duration");
Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index c6795111d496..5a0bbf7d9436 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -95,11 +95,8 @@ public final class StepSegment extends VibrationEffectSegment {
/** @hide */
@Override
public void validate() {
- Preconditions.checkArgumentNonNegative(mFrequencyHz,
- "Frequencies must all be >= 0, got " + mFrequencyHz);
- Preconditions.checkArgumentFinite(mFrequencyHz, "frequencyHz");
- Preconditions.checkArgumentNonnegative(mDuration,
- "Durations must all be >= 0, got " + mDuration);
+ VibrationEffectSegment.checkFrequencyArgument(mFrequencyHz, "frequencyHz");
+ VibrationEffectSegment.checkDurationArgument(mDuration, "duration");
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
Preconditions.checkArgumentInRange(mAmplitude, 0f, 1f, "amplitude");
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 979c4472eb9b..be1055362f1c 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -112,6 +112,43 @@ public abstract class VibrationEffectSegment implements Parcelable {
@NonNull
public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
+ /**
+ * Checks the given frequency argument is valid to represent a vibration effect frequency in
+ * hertz, i.e. a finite non-negative value.
+ *
+ * @param value the frequency argument value to be checked
+ * @param name the argument name for the error message.
+ *
+ * @hide
+ */
+ public static void checkFrequencyArgument(float value, @NonNull String name) {
+ // Similar to combining Preconditions checkArgumentFinite + checkArgumentNonnegative,
+ // but this implementation doesn't create the error message unless a check fail.
+ if (Float.isNaN(value)) {
+ throw new IllegalArgumentException(name + " must not be NaN");
+ }
+ if (Float.isInfinite(value)) {
+ throw new IllegalArgumentException(name + " must not be infinite");
+ }
+ if (value < 0) {
+ throw new IllegalArgumentException(name + " must be >= 0, got " + value);
+ }
+ }
+
+ /**
+ * Checks the given duration argument is valid, i.e. a non-negative value.
+ *
+ * @param value the duration value to be checked
+ * @param name the argument name for the error message.
+ *
+ * @hide
+ */
+ public static void checkDurationArgument(long value, @NonNull String name) {
+ if (value < 0) {
+ throw new IllegalArgumentException(name + " must be >= 0, got " + value);
+ }
+ }
+
@NonNull
public static final Creator<VibrationEffectSegment> CREATOR =
new Creator<VibrationEffectSegment>() {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 52b6944ba940..afd12835283b 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -407,6 +407,13 @@ public final class DeviceConfig {
public static final String NAMESPACE_SCHEDULER = "scheduler";
/**
+ * Namespace for all SdkSandbox related features.
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SDK_SANDBOX = "sdk_sandbox";
+
+ /**
* Namespace for settings statistics features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 11b5d44de3b3..8feff16c70a2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9971,6 +9971,13 @@ public final class Settings {
"lockscreen_use_double_line_clock";
/**
+ * Whether to show the vibrate icon in the Status Bar (default off)
+ *
+ * @hide
+ */
+ public static final String STATUS_BAR_SHOW_VIBRATE_ICON = "status_bar_show_vibrate_icon";
+
+ /**
* Specifies whether the web action API is enabled.
*
* @hide
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index cfb690916ecc..b701e07e22e6 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -905,7 +905,7 @@ public final class Dataset implements Parcelable {
if (field == null) {
setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
} else {
- final DatasetFieldFilter filter = field.getFilter();
+ final DatasetFieldFilter filter = field.getDatasetFieldFilter();
final Presentations presentations = field.getPresentations();
if (presentations == null) {
setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
diff --git a/core/java/android/service/autofill/Field.java b/core/java/android/service/autofill/Field.java
index b7c0d82f41f5..8c905a6faa0d 100644
--- a/core/java/android/service/autofill/Field.java
+++ b/core/java/android/service/autofill/Field.java
@@ -18,7 +18,6 @@ package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -86,11 +85,21 @@ public final class Field {
* @see Dataset.DatasetFieldFilter
* @hide
*/
- public @Nullable Dataset.DatasetFieldFilter getFilter() {
+ public @Nullable Dataset.DatasetFieldFilter getDatasetFieldFilter() {
return mFilter;
}
/**
+ * Regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ */
+ public @Nullable Pattern getFilter() {
+ return mFilter == null ? null : mFilter.pattern;
+ }
+
+ /**
* The presentations used to visualize this field in Autofill UI.
*/
public @Nullable Presentations getPresentations() {
@@ -127,7 +136,6 @@ public final class Field {
* approach when {@code value} is not {@code null} and field contains sensitive data
* such as passwords).
*/
- @SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder setFilter(@Nullable Pattern value) {
checkNotUsed();
mFilter = new Dataset.DatasetFieldFilter(value);
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index db622d39b785..86707598255d 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -231,7 +231,7 @@ public class DreamService extends Service implements Window.Callback {
* The default value for whether to show complications on the overlay.
* @hide
*/
- public static final boolean DEFAULT_SHOW_COMPLICATIONS = true;
+ public static final boolean DEFAULT_SHOW_COMPLICATIONS = false;
private final IDreamManager mDreamManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 188bc3f51c52..4541f3afa428 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -95,7 +95,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
- DEFAULT_FLAGS.put("settings_search_always_expand", "false");
+ DEFAULT_FLAGS.put("settings_search_always_expand", "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "false");
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 4ac3178ecb4c..8604078b5ae5 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -81,7 +81,16 @@ public class NtpTrustedTime implements TrustedTime {
/** Calculates and returns the age of this result. */
public long getAgeMillis() {
- return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis;
+ return getAgeMillis(SystemClock.elapsedRealtime());
+ }
+
+ /**
+ * Calculates and returns the age of this result relative to currentElapsedRealtimeMillis.
+ *
+ * @param currentElapsedRealtimeMillis - reference elapsed real time
+ */
+ public long getAgeMillis(long currentElapsedRealtimeMillis) {
+ return currentElapsedRealtimeMillis - mElapsedRealtimeMillis;
}
@Override
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 53b842a0f3a2..f8a1b45a1c75 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -955,4 +955,10 @@ interface IWindowManager
* @hide
*/
Bitmap snapshotTaskForRecents(int taskId);
+
+ /**
+ * Informs the system whether the recents app is currently behind the system bars. If so,
+ * means the recents app can control the SystemUI flags, and vice-versa.
+ */
+ void setRecentsAppBehindSystemBars(boolean behindSystemBars);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a266a28dcaca..5c7c844ae773 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -290,7 +290,8 @@ interface IWindowSession {
/**
* Called when the keep-clear areas for this window have changed.
*/
- oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> keepClearRects);
+ oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> restricted,
+ in List<Rect> unrestricted);
/**
* Request the server to call setInputWindowInfo on a given Surface, and return
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index b1582cf9f023..6aab6359d23e 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -98,6 +98,13 @@ public class InsetsSourceConsumer {
*/
private boolean mIsAnimationPending;
+ /**
+ * @param type The {@link InternalInsetsType} of the consumed insets.
+ * @param state The current {@link InsetsState} of the consumed insets.
+ * @param transactionSupplier The source of new {@link Transaction} instances. The supplier
+ * must provide *new* instances, which will be explicitly closed by this class.
+ * @param controller The {@link InsetsController} to use for insets interaction.
+ */
public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
Supplier<Transaction> transactionSupplier, InsetsController controller) {
mType = type;
@@ -390,16 +397,17 @@ public class InsetsSourceConsumer {
return;
}
- final Transaction t = mTransactionSupplier.get();
- if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + mRequestedVisible);
- if (mRequestedVisible) {
- t.show(mSourceControl.getLeash());
- } else {
- t.hide(mSourceControl.getLeash());
+ try (Transaction t = mTransactionSupplier.get()) {
+ if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + mRequestedVisible);
+ if (mRequestedVisible) {
+ t.show(mSourceControl.getLeash());
+ } else {
+ t.hide(mSourceControl.getLeash());
+ }
+ // Ensure the alpha value is aligned with the actual requested visibility.
+ t.setAlpha(mSourceControl.getLeash(), mRequestedVisible ? 1 : 0);
+ t.apply();
}
- // Ensure the alpha value is aligned with the actual requested visibility.
- t.setAlpha(mSourceControl.getLeash(), mRequestedVisible ? 1 : 0);
- t.apply();
onPerceptible(mRequestedVisible);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 55300b3fec36..9aa243b241f7 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -133,8 +133,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private boolean mDisableBackgroundLayer = false;
/**
- * We use this lock to protect access to mSurfaceControl. Both are accessed on the UI
- * thread and the render thread via RenderNode.PositionUpdateListener#positionLost.
+ * We use this lock to protect access to mSurfaceControl and
+ * SurfaceViewPositionUpdateListener#mPositionChangedTransaction. Both are accessed on the UI
+ * thread and the render thread.
*/
final Object mSurfaceControlLock = new Object();
final Rect mTmpRect = new Rect();
@@ -223,6 +224,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private final SurfaceControl.Transaction mFrameCallbackTransaction =
new SurfaceControl.Transaction();
+ /**
+ * A temporary transaction holder that should only be used when applying right away. There
+ * should be no assumption about thread safety for this transaction.
+ */
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+
private int mParentSurfaceSequenceId;
private RemoteAccessibilityController mRemoteAccessibilityController =
@@ -753,7 +760,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mBlastBufferQueue = null;
}
- final Transaction transaction = new Transaction();
+ Transaction transaction = new Transaction();
if (mSurfaceControl != null) {
transaction.remove(mSurfaceControl);
mSurfaceControl = null;
@@ -783,18 +790,22 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// synchronously otherwise we may see flickers.
// When the listener is updated, we will get at least a single position update call so we can
// guarantee any changes we post will be applied.
- private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight) {
+ private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight,
+ Transaction geometryTransaction) {
if (mPositionListener != null) {
mRenderNode.removePositionUpdateListener(mPositionListener);
+ synchronized (mSurfaceControlLock) {
+ geometryTransaction = mPositionListener.getTransaction().merge(geometryTransaction);
+ }
}
mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight,
- mSurfaceControl);
+ geometryTransaction);
mRenderNode.addPositionUpdateListener(mPositionListener);
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
boolean creating, boolean sizeChanged, boolean hintChanged,
- Transaction surfaceUpdateTransaction) {
+ Transaction geometryTransaction) {
boolean realSizeChanged = false;
mSurfaceLock.lock();
@@ -809,60 +820,59 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// SurfaceChangedCallback to update the relative z. This is needed so that
// we do not change the relative z before the server is ready to swap the
// parent surface.
- if (creating) {
- updateRelativeZ(surfaceUpdateTransaction);
- if (mSurfacePackage != null) {
- reparentSurfacePackage(surfaceUpdateTransaction, mSurfacePackage);
- }
+ if (creating || (mParentSurfaceSequenceId == viewRoot.getSurfaceSequenceId())) {
+ updateRelativeZ(mTmpTransaction);
}
mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
if (mViewVisibility) {
- surfaceUpdateTransaction.show(mSurfaceControl);
+ geometryTransaction.show(mSurfaceControl);
} else {
- surfaceUpdateTransaction.hide(mSurfaceControl);
+ geometryTransaction.hide(mSurfaceControl);
}
+ if (mSurfacePackage != null) {
+ reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
+ }
-
- updateBackgroundVisibility(surfaceUpdateTransaction);
- updateBackgroundColor(surfaceUpdateTransaction);
+ updateBackgroundVisibility(mTmpTransaction);
+ updateBackgroundColor(mTmpTransaction);
if (mUseAlpha) {
float alpha = getFixedAlpha();
- surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
+ mTmpTransaction.setAlpha(mSurfaceControl, alpha);
mSurfaceAlpha = alpha;
}
- surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+ geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if ((sizeChanged || hintChanged) && !creating) {
- setBufferSize(surfaceUpdateTransaction);
+ setBufferSize(geometryTransaction);
}
if (sizeChanged || creating || !isHardwareAccelerated()) {
+ onSetSurfacePositionAndScaleRT(geometryTransaction, mSurfaceControl,
+ mScreenRect.left, /*positionLeft*/
+ mScreenRect.top /*positionTop*/ ,
+ mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+ mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
// Set a window crop when creating the surface or changing its size to
// crop the buffer to the surface size since the buffer producer may
// use SCALING_MODE_SCALE and submit a larger size than the surface
// size.
if (mClipSurfaceToBounds && mClipBounds != null) {
- surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+ geometryTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
} else {
- surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+ geometryTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
}
- surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
+ geometryTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
if (isHardwareAccelerated()) {
// This will consume the passed in transaction and the transaction will be
// applied on a render worker thread.
- replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight);
- } else {
- onSetSurfacePositionAndScale(surfaceUpdateTransaction, mSurfaceControl,
- mScreenRect.left /*positionLeft*/,
- mScreenRect.top /*positionTop*/,
- mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
- mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+ replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight,
+ geometryTransaction);
}
if (DEBUG_POSITION) {
Log.d(TAG, String.format(
@@ -874,7 +884,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
}
}
- applyTransactionOnVriDraw(surfaceUpdateTransaction);
+ mTmpTransaction.merge(geometryTransaction);
+ mTmpTransaction.apply();
updateEmbeddedAccessibilityMatrix();
mSurfaceFrame.left = 0;
@@ -982,17 +993,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
// Collect all geometry changes and apply these changes on the RenderThread worker
// via the RenderNode.PositionUpdateListener.
- final Transaction surfaceUpdateTransaction = new Transaction();
+ final Transaction geometryTransaction = new Transaction();
if (creating) {
updateOpaqueFlag();
final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
- createBlastSurfaceControls(viewRoot, name, surfaceUpdateTransaction);
+ createBlastSurfaceControls(viewRoot, name, geometryTransaction);
} else if (mSurfaceControl == null) {
return;
}
final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
+ translator, creating, sizeChanged, hintChanged, geometryTransaction);
final boolean redrawNeeded = sizeChanged || creating || hintChanged
|| (mVisible && !mDrawFinished);
@@ -1128,7 +1139,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
*
*/
private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name,
- Transaction surfaceUpdateTransaction) {
+ Transaction geometryTransaction) {
if (mSurfaceControl == null) {
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
@@ -1151,10 +1162,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
.build();
} else {
// update blast layer
- surfaceUpdateTransaction
+ mTmpTransaction
.setOpaque(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
.setSecure(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.SECURE) != 0)
- .show(mBlastSurfaceControl);
+ .show(mBlastSurfaceControl)
+ .apply();
}
if (mBackgroundControl == null) {
@@ -1201,7 +1213,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
*
* @hide
*/
- protected void onSetSurfacePositionAndScale(@NonNull Transaction transaction,
+ protected void onSetSurfacePositionAndScaleRT(@NonNull Transaction transaction,
@NonNull SurfaceControl surface, int positionLeft, int positionTop,
float postScaleX, float postScaleY) {
transaction.setPosition(surface, positionLeft, positionTop);
@@ -1214,14 +1226,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (mSurfaceControl == null) {
return;
}
- final Transaction transaction = new Transaction();
- onSetSurfacePositionAndScale(transaction, mSurfaceControl,
+ onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
mScreenRect.left, /*positionLeft*/
mScreenRect.top/*positionTop*/ ,
mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
- applyTransactionOnVriDraw(transaction);
- invalidate();
+ mTmpTransaction.apply();
}
/**
@@ -1243,57 +1253,66 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- private final Rect mRTLastReportedPosition = new Rect();
- private final Point mRTLastReportedSurfaceSize = new Point();
+ private Rect mRTLastReportedPosition = new Rect();
+ private Point mRTLastReportedSurfaceSize = new Point();
private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
- private final int mRtSurfaceWidth;
- private final int mRtSurfaceHeight;
+ int mRtSurfaceWidth = -1;
+ int mRtSurfaceHeight = -1;
private final SurfaceControl.Transaction mPositionChangedTransaction =
new SurfaceControl.Transaction();
- private final SurfaceControl mRtSurfaceControl = new SurfaceControl();
+ boolean mPendingTransaction = false;
SurfaceViewPositionUpdateListener(int surfaceWidth, int surfaceHeight,
- SurfaceControl surfaceControl) {
+ @Nullable Transaction t) {
mRtSurfaceWidth = surfaceWidth;
mRtSurfaceHeight = surfaceHeight;
- mRtSurfaceControl.copyFrom(surfaceControl, "SurfaceViewPositionUpdateListener");
+ if (t != null) {
+ mPositionChangedTransaction.merge(t);
+ mPendingTransaction = true;
+ }
}
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
- if (mRTLastReportedPosition.left == left
- && mRTLastReportedPosition.top == top
- && mRTLastReportedPosition.right == right
- && mRTLastReportedPosition.bottom == bottom
- && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
- && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight) {
- return;
- }
- try {
- if (DEBUG_POSITION) {
- Log.d(TAG, String.format(
- "%d updateSurfacePosition RenderWorker, frameNr = %d, "
- + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
- System.identityHashCode(SurfaceView.this), frameNumber,
- left, top, right, bottom, mRtSurfaceWidth, mRtSurfaceHeight));
+ synchronized(mSurfaceControlLock) {
+ if (mSurfaceControl == null) {
+ return;
}
- mRTLastReportedPosition.set(left, top, right, bottom);
- mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
- onSetSurfacePositionAndScale(mPositionChangedTransaction, mRtSurfaceControl,
- mRTLastReportedPosition.left /*positionLeft*/,
- mRTLastReportedPosition.top /*positionTop*/,
- mRTLastReportedPosition.width()
- / (float) mRtSurfaceWidth /*postScaleX*/,
- mRTLastReportedPosition.height()
- / (float) mRtSurfaceHeight /*postScaleY*/);
- if (mViewVisibility) {
- // b/131239825
- mPositionChangedTransaction.show(mRtSurfaceControl);
+ if (mRTLastReportedPosition.left == left
+ && mRTLastReportedPosition.top == top
+ && mRTLastReportedPosition.right == right
+ && mRTLastReportedPosition.bottom == bottom
+ && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
+ && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight
+ && !mPendingTransaction) {
+ return;
+ }
+ try {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format(
+ "%d updateSurfacePosition RenderWorker, frameNr = %d, "
+ + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+ System.identityHashCode(SurfaceView.this), frameNumber,
+ left, top, right, bottom, mRtSurfaceWidth, mRtSurfaceHeight));
+ }
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
+ onSetSurfacePositionAndScaleRT(mPositionChangedTransaction, mSurfaceControl,
+ mRTLastReportedPosition.left /*positionLeft*/,
+ mRTLastReportedPosition.top /*positionTop*/,
+ mRTLastReportedPosition.width()
+ / (float) mRtSurfaceWidth /*postScaleX*/,
+ mRTLastReportedPosition.height()
+ / (float) mRtSurfaceHeight /*postScaleY*/);
+ if (mViewVisibility) {
+ mPositionChangedTransaction.show(mSurfaceControl);
+ }
+ applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
+ mPendingTransaction = false;
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception from repositionChild", ex);
}
- applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
- } catch (Exception ex) {
- Log.e(TAG, "Exception from repositionChild", ex);
}
}
@@ -1302,7 +1321,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
float vecX, float vecY, float maxStretchX, float maxStretchY,
float childRelativeLeft, float childRelativeTop, float childRelativeRight,
float childRelativeBottom) {
- mRtTransaction.setStretchEffect(mRtSurfaceControl, width, height, vecX, vecY,
+ mRtTransaction.setStretchEffect(mSurfaceControl, width, height, vecX, vecY,
maxStretchX, maxStretchY, childRelativeLeft, childRelativeTop,
childRelativeRight, childRelativeBottom);
applyOrMergeTransaction(mRtTransaction, frameNumber);
@@ -1317,14 +1336,28 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mRTLastReportedPosition.setEmpty();
mRTLastReportedSurfaceSize.set(-1, -1);
- // positionLost can be called while UI thread is un-paused.
+ /**
+ * positionLost can be called while UI thread is un-paused so we
+ * need to hold the lock here.
+ */
synchronized (mSurfaceControlLock) {
- if (mSurfaceControl == null) return;
- // b/131239825
+ if (mPendingTransaction) {
+ Log.w(TAG, System.identityHashCode(SurfaceView.this)
+ + "Pending transaction cleared.");
+ mPositionChangedTransaction.clear();
+ mPendingTransaction = false;
+ }
+ if (mSurfaceControl == null) {
+ return;
+ }
mRtTransaction.hide(mSurfaceControl);
applyOrMergeTransaction(mRtTransaction, frameNumber);
}
}
+
+ public Transaction getTransaction() {
+ return mPositionChangedTransaction;
+ }
}
private SurfaceViewPositionUpdateListener mPositionListener = null;
@@ -1371,10 +1404,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
- final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
- setResizeBackgroundColor(transaction, bgColor);
- applyTransactionOnVriDraw(transaction);
- invalidate();
+ setResizeBackgroundColor(mTmpTransaction, bgColor);
+ mTmpTransaction.apply();
}
/**
@@ -1789,4 +1820,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
t.apply();
}
}
+
+ /**
+ * @hide
+ */
+ public void syncNextFrame(Transaction t) {
+ mBlastBufferQueue.setSyncTransaction(t);
+ }
}
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 3e2110341693..e9c937cc0f9b 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -65,10 +65,12 @@ public class SyncRtSurfaceTransactionApplier {
applyParams(t, params);
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
- if (mTargetSc == null || !mTargetSc.isValid()) {
- return;
+ if (mTargetSc != null && mTargetSc.isValid()) {
+ applyTransaction(t, frame);
}
- applyTransaction(t, frame);
+ // The transaction was either dropped, successfully applied, or merged with a future
+ // transaction, so we can safely release its resources.
+ t.close();
});
// Make sure a frame gets scheduled.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 553c537d9b95..8b3a29a7aa6f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -46,9 +46,11 @@ import android.annotation.IntRange;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.Size;
import android.annotation.StyleRes;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UiContext;
import android.annotation.UiThread;
@@ -4744,6 +4746,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private List<Rect> mSystemGestureExclusionRects = null;
private List<Rect> mKeepClearRects = null;
+ private List<Rect> mUnrestrictedKeepClearRects = null;
private boolean mPreferKeepClear = false;
private Rect mHandwritingArea = null;
@@ -11729,6 +11732,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final ListenerInfo info = getListenerInfo();
if (getSystemGestureExclusionRects().isEmpty()
&& collectPreferKeepClearRects().isEmpty()
+ && collectUnrestrictedPreferKeepClearRects().isEmpty()
&& (info.mHandwritingArea == null || !isAutoHandwritingEnabled())) {
if (info.mPositionUpdateListener != null) {
mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
@@ -11871,6 +11875,52 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return Collections.emptyList();
}
+ /**
+ * Set a preference to keep the provided rects clear from floating windows above this
+ * view's window. This informs the system that these rects are considered vital areas for the
+ * user and that ideally they should not be covered. Setting this is only appropriate for UI
+ * where the user would likely take action to uncover it.
+ * <p>
+ * Note: The difference with {@link #setPreferKeepClearRects} is that the system won't apply
+ * restrictions to the rects set here.
+ * <p>
+ * @see #setPreferKeepClear
+ * @see #getPreferKeepClearRects
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS)
+ public final void setUnrestrictedPreferKeepClearRects(@NonNull List<Rect> rects) {
+ final ListenerInfo info = getListenerInfo();
+ if (info.mUnrestrictedKeepClearRects != null) {
+ info.mUnrestrictedKeepClearRects.clear();
+ info.mUnrestrictedKeepClearRects.addAll(rects);
+ } else {
+ info.mUnrestrictedKeepClearRects = new ArrayList<>(rects);
+ }
+ updatePositionUpdateListener();
+ postUpdate(this::updateKeepClearRects);
+ }
+
+ /**
+ * @return the list of rects, set by {@link #setPreferKeepClearRects}.
+ *
+ * @see #setPreferKeepClearRects
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public final List<Rect> getUnrestrictedPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null && info.mKeepClearRects != null) {
+ return new ArrayList(info.mUnrestrictedKeepClearRects);
+ }
+
+ return Collections.emptyList();
+ }
+
void updateKeepClearRects() {
final AttachInfo ai = mAttachInfo;
if (ai != null) {
@@ -11899,6 +11949,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Retrieve the list of unrestricted areas within this view's post-layout coordinate space
+ * which the system will try to not cover with other floating elements, like the pip window.
+ */
+ @NonNull
+ List<Rect> collectUnrestrictedPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null && info.mUnrestrictedKeepClearRects != null) {
+ return info.mUnrestrictedKeepClearRects;
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
* Set a list of handwriting areas in this view. If there is any stylus {@link MotionEvent}
* occurs within those areas, it will trigger stylus handwriting mode. This can be disabled by
* disabling the auto handwriting initiation by calling
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index feb17f54acd6..d3d36255d05a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -76,6 +76,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -195,6 +196,7 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.ClientWindowFrames;
+import android.window.SurfaceSyncer;
import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
@@ -219,6 +221,7 @@ import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -776,6 +779,8 @@ public final class ViewRootImpl implements ViewParent,
new ViewRootRectTracker(v -> v.getSystemGestureExclusionRects());
private final ViewRootRectTracker mKeepClearRectsTracker =
new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
+ private final ViewRootRectTracker mUnrestrictedKeepClearRectsTracker =
+ new ViewRootRectTracker(v -> v.collectUnrestrictedPreferKeepClearRects());
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -2461,8 +2466,9 @@ public final class ViewRootImpl implements ViewParent,
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
+ ", desiredWindowWidth=" + desiredWindowWidth);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
- childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+ childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags);
+ childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
+ lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
@@ -2475,7 +2481,7 @@ public final class ViewRootImpl implements ViewParent,
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
- childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
+ childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
@@ -2488,8 +2494,10 @@ public final class ViewRootImpl implements ViewParent,
}
if (!goodMeasure) {
- childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+ childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
+ lp.privateFlags);
+ childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
+ lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
@@ -3149,8 +3157,10 @@ public final class ViewRootImpl implements ViewParent,
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
updatedConfiguration) {
- int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
- int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
+ int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
+ lp.privateFlags);
+ int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,
+ lp.privateFlags);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
@@ -3950,31 +3960,28 @@ public final class ViewRootImpl implements ViewParent,
* Figures out the measure spec for the root view in a window based on it's
* layout params.
*
- * @param windowSize
- * The available width or height of the window
- *
- * @param rootDimension
- * The layout params for one dimension (width or height) of the
- * window.
- *
+ * @param windowSize The available width or height of the window.
+ * @param measurement The layout width or height requested in the layout params.
+ * @param privateFlags The private flags in the layout params of the window.
* @return The measure spec to use to measure the root view.
*/
- private static int getRootMeasureSpec(int windowSize, int rootDimension) {
+ private static int getRootMeasureSpec(int windowSize, int measurement, int privateFlags) {
int measureSpec;
+ final int rootDimension = (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0
+ ? MATCH_PARENT : measurement;
switch (rootDimension) {
-
- case ViewGroup.LayoutParams.MATCH_PARENT:
- // Window can't resize. Force root view to be windowSize.
- measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
- break;
- case ViewGroup.LayoutParams.WRAP_CONTENT:
- // Window can resize. Set max size for root view.
- measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
- break;
- default:
- // Window wants to be an exact size. Force root view to be that size.
- measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
- break;
+ case ViewGroup.LayoutParams.MATCH_PARENT:
+ // Window can't resize. Force root view to be windowSize.
+ measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
+ break;
+ case ViewGroup.LayoutParams.WRAP_CONTENT:
+ // Window can resize. Set max size for root view.
+ measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
+ break;
+ default:
+ // Window wants to be an exact size. Force root view to be that size.
+ measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
+ break;
}
return measureSpec;
}
@@ -4174,7 +4181,7 @@ public final class ViewRootImpl implements ViewParent,
+ " didProduceBuffer=" + didProduceBuffer);
}
- Transaction tmpTransaction = new Transaction();
+ final Transaction tmpTransaction = new Transaction();
tmpTransaction.merge(mRtBLASTSyncTransaction);
// If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
@@ -4205,6 +4212,7 @@ public final class ViewRootImpl implements ViewParent,
blastSyncConsumer.accept(mSurfaceChangedTransaction);
}
}
+ tmpTransaction.close();
if (reportNextDraw) {
pendingDrawFinished();
@@ -4874,14 +4882,26 @@ public final class ViewRootImpl implements ViewParent,
*/
void updateKeepClearRectsForView(View view) {
mKeepClearRectsTracker.updateRectsForView(view);
+ mUnrestrictedKeepClearRectsTracker.updateRectsForView(view);
mHandler.sendEmptyMessage(MSG_KEEP_CLEAR_RECTS_CHANGED);
}
void keepClearRectsChanged() {
- final List<Rect> rectsForWindowManager = mKeepClearRectsTracker.computeChangedRects();
- if (rectsForWindowManager != null && mView != null) {
+ List<Rect> restrictedKeepClearRects = mKeepClearRectsTracker.computeChangedRects();
+ List<Rect> unrestrictedKeepClearRects =
+ mUnrestrictedKeepClearRectsTracker.computeChangedRects();
+ if ((restrictedKeepClearRects != null || unrestrictedKeepClearRects != null)
+ && mView != null) {
+ if (restrictedKeepClearRects == null) {
+ restrictedKeepClearRects = Collections.emptyList();
+ }
+ if (unrestrictedKeepClearRects == null) {
+ unrestrictedKeepClearRects = Collections.emptyList();
+ }
+
try {
- mWindowSession.reportKeepClearAreasChanged(mWindow, rectsForWindowManager);
+ mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
+ unrestrictedKeepClearRects);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -10873,4 +10893,71 @@ public final class ViewRootImpl implements ViewParent,
IWindowSession getWindowSession() {
return mWindowSession;
}
+
+ private void registerCallbacksForSync(
+ final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ if (!isHardwareEnabled()) {
+ // TODO: correctly handle when hardware disabled
+ syncBufferCallback.onBufferReady(null);
+ return;
+ }
+
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
+
+ final Transaction t = new Transaction();
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ syncBufferCallback.onBufferReady(t);
+ return null;
+ }
+
+ mBlastBufferQueue.setSyncTransaction(t);
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
+
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback"
+ + " lastAttemptedDrawFrameNum=" + frame
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect
+ // the next draw attempt. The next transaction and transaction complete callback
+ // were only set for the current draw attempt.
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.setSyncTransaction(null);
+ // Gather the transactions that were sent to mergeWithNextTransaction
+ // since the frame didn't draw on this vsync. It's possible the frame will
+ // draw later, but it's better to not be sync than to block on a frame that
+ // may never come.
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ }
+
+ syncBufferCallback.onBufferReady(t);
+ };
+ }
+ });
+ }
+
+ public final SurfaceSyncer.SyncTarget mSyncTarget =
+ syncBufferCallback -> registerCallbacksForSync(syncBufferCallback);
}
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 27f89ec4ac79..ad9f21b4fa70 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.Gravity.DISPLAY_CLIP_HORIZONTAL;
+import static android.view.Gravity.DISPLAY_CLIP_VERTICAL;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -29,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
@@ -250,11 +253,39 @@ public class WindowLayout {
Gravity.apply(attrs.gravity, w, h, outParentFrame,
(int) (x + attrs.horizontalMargin * pw),
(int) (y + attrs.verticalMargin * ph), outFrame);
+
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
+ if ((attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0
+ && !cutout.isEmpty()) {
+ // If the actual frame covering a display cutout, and the window is requesting to extend
+ // it's requested frame, re-do the frame calculation after getting the new requested
+ // size.
+ mTempRect.set(outFrame);
+ // Do nothing if the display cutout and window don't overlap entirely. This may happen
+ // when the cutout is not on the same side with the window.
+ boolean shouldExpand = false;
+ final Rect [] cutoutBounds = cutout.getBoundingRectsAll();
+ for (Rect cutoutBound : cutoutBounds) {
+ if (cutoutBound.isEmpty()) continue;
+ if (mTempRect.contains(cutoutBound) || cutoutBound.contains(mTempRect)) {
+ shouldExpand = true;
+ break;
+ }
+ }
+ if (shouldExpand) {
+ // Try to fit move the bar to avoid the display cutout first. Make sure the clip
+ // flags are not set to make the window move.
+ final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL;
+ Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe,
+ mTempRect);
+ outFrame.union(mTempRect);
+ }
+ }
+
if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle()
+ " outFrame=" + outFrame.toShortString()
+ " outParentFrame=" + outParentFrame.toShortString()
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8ee3e432cbbb..dbfae46a6e72 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2385,6 +2385,16 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 0x00001000;
/**
+ * Flag to indicate that the window frame should be the requested frame adding the display
+ * cutout frame. This will only be applied if a specific size smaller than the parent frame
+ * is given, and the window is covering the display cutout. The extended frame will not be
+ * larger than the parent frame.
+ *
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 0x00002000;
+
+ /**
* Flag that will make window ignore app visibility and instead depend purely on the decor
* view visibility for determining window visibility. This is used by recents to keep
* drawing after it launches an app.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 93cb0dd7a234..2dc5fbd5439f 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -752,6 +752,14 @@ public final class WindowManagerGlobal {
public void removeWindowlessRoot(ViewRootImpl impl) {
synchronized (mLock) {
mWindowlessRoots.remove(impl);
+ }
+ }
+
+ public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
+ try {
+ getWindowManagerService().setRecentsAppBehindSystemBars(behindSystemBars);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index c81b8ccf94d6..a270c9283ddc 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -31,6 +31,7 @@ import android.window.ClientWindowFrames;
import android.window.IOnBackInvokedCallback;
import java.util.HashMap;
+import java.util.List;
import java.util.Objects;
/**
@@ -473,12 +474,12 @@ public class WindowlessWindowManager implements IWindowSession {
@Override
public void reportSystemGestureExclusionChanged(android.view.IWindow window,
- java.util.List<android.graphics.Rect> exclusionRects) {
+ List<Rect> exclusionRects) {
}
@Override
- public void reportKeepClearAreasChanged(android.view.IWindow window,
- java.util.List<android.graphics.Rect> exclusionRects) {
+ public void reportKeepClearAreasChanged(android.view.IWindow window, List<Rect> restrictedRects,
+ List<Rect> unrestrictedRects) {
}
@Override
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index 67d96678f420..62d029bd1be6 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -32,7 +32,7 @@ oneway interface IWindowMagnificationConnection {
/**
* Enables window magnification on specified display with given center and scale and animation.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id.
* @param scale magnification scale.
* @param centerX the screen-relative X coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
@@ -51,7 +51,7 @@ oneway interface IWindowMagnificationConnection {
/**
* Sets the scale of the window magnifier on specified display.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id.
* @param scale magnification scale.
*/
void setScale(int displayId, float scale);
@@ -59,7 +59,7 @@ oneway interface IWindowMagnificationConnection {
/**
* Disables window magnification on specified display with animation.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id.
* @param callback The callback called when the animation is completed or interrupted.
*/
void disableWindowMagnification(int displayId,
@@ -68,6 +68,7 @@ oneway interface IWindowMagnificationConnection {
/**
* Moves the window magnifier on the specified display. It has no effect while animating.
*
+ * @param displayId the logical display id.
* @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
* current screen pixels.
* @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
@@ -76,9 +77,20 @@ oneway interface IWindowMagnificationConnection {
void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
/**
+ * Moves the window magnifier on the given display.
+ *
+ * @param displayId the logical display id.
+ * @param positionX the x-axis position of the center of the magnified source bounds.
+ * @param positionY the y-axis position of the center of the magnified source bounds.
+ * @param callback the callback called when the animation is completed or interrupted.
+ */
+ void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
+ in IRemoteMagnificationAnimationCallback callback);
+
+ /**
* Requests System UI show magnification mode button UI on the specified display.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id.
* @param magnificationMode the current magnification mode.
*/
void showMagnificationButton(int displayId, int magnificationMode);
@@ -86,7 +98,7 @@ oneway interface IWindowMagnificationConnection {
/**
* Requests System UI remove magnification mode button UI on the specified display.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id.
*/
void removeMagnificationButton(int displayId);
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index 722546eb06e4..adfeb6d11008 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -68,12 +68,10 @@ import android.graphics.Rect;
void onAccessibilityActionPerformed(int displayId);
/**
- * Called when the user is performing dragging gesture. It is started after the offset
- * between the down location and the move event location exceed
- * {@link ViewConfiguration#getScaledTouchSlop()}.
+ * Called when the user is performing move action.
*
* @param displayId The logical display id.
*/
- void onDrag(int displayId);
+ void onMove(int displayId);
}
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 2bfbe4bdfba7..bc7a5fda6f7a 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -25,6 +25,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.Zygote;
/** @hide */
public class WebViewZygote {
@@ -127,13 +128,15 @@ public class WebViewZygote {
try {
String abi = sPackage.applicationInfo.primaryCpuAbi;
+ int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
+ sPackage.applicationInfo, null);
sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.WebViewZygoteInit",
"webview_zygote",
Process.WEBVIEW_ZYGOTE_UID,
Process.WEBVIEW_ZYGOTE_UID,
null, // gids
- 0, // runtimeFlags
+ runtimeFlags,
"webview_zygote", // seInfo
abi, // abi
TextUtils.join(",", Build.SUPPORTED_ABIS),
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index e4f483a29343..9712311aab7c 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -230,9 +230,8 @@ public class InlineContentView extends ViewGroup {
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes) {
- // b/219807628
@Override
- protected void onSetSurfacePositionAndScale(
+ protected void onSetSurfacePositionAndScaleRT(
@NonNull SurfaceControl.Transaction transaction,
@NonNull SurfaceControl surface, int positionLeft, int positionTop,
float postScaleX, float postScaleY) {
@@ -249,7 +248,7 @@ public class InlineContentView extends ViewGroup {
postScaleX = InlineContentView.this.getScaleX();
postScaleY = InlineContentView.this.getScaleY();
- super.onSetSurfacePositionAndScale(transaction, surface, positionLeft,
+ super.onSetSurfacePositionAndScaleRT(transaction, surface, positionLeft,
positionTop, postScaleX, postScaleY);
}
};
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 34a34180b227..232248b6fab3 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -65,6 +65,7 @@ import com.android.internal.util.ContrastColorUtil;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;
+import java.util.function.LongConsumer;
/**
* <p>The view which allows an activity to customize its splash screen exit animation.</p>
@@ -234,7 +235,7 @@ public final class SplashScreenView extends FrameLayout {
/**
* Set the animation duration if icon is animatable.
*/
- public Builder setAnimationDurationMillis(int duration) {
+ public Builder setAnimationDurationMillis(long duration) {
mIconAnimationDuration = Duration.ofMillis(duration);
return this;
}
@@ -521,8 +522,11 @@ public final class SplashScreenView extends FrameLayout {
});
}
- private void animationStartCallback() {
+ private void animationStartCallback(long animDuration) {
mIconAnimationStart = Instant.now();
+ if (animDuration > 0) {
+ mIconAnimationDuration = Duration.ofMillis(animDuration);
+ }
}
/**
@@ -693,9 +697,8 @@ public final class SplashScreenView extends FrameLayout {
* Prepare the animation if this drawable also be animatable.
* @param duration The animation duration.
* @param startListener The callback listener used to receive the start of the animation.
- * @return true if this drawable object can also be animated and it can be played now.
*/
- boolean prepareAnimate(long duration, Runnable startListener);
+ void prepareAnimate(long duration, LongConsumer startListener);
/**
* Stop animation.
diff --git a/core/java/android/window/SurfaceSyncer.java b/core/java/android/window/SurfaceSyncer.java
new file mode 100644
index 000000000000..a5930757cae6
--- /dev/null
+++ b/core/java/android/window/SurfaceSyncer.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UiThread;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceView;
+import android.view.View;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Used to organize syncs for surfaces.
+ *
+ * The SurfaceSyncer allows callers to add desired syncs into a set and wait for them to all
+ * complete before getting a callback. The purpose of the Syncer is to be an accounting mechanism
+ * so each sync implementation doesn't need to handle it themselves. The Syncer class is used the
+ * following way.
+ *
+ * 1. {@link #setupSync(Runnable)} is called
+ * 2. {@link #addToSync(int, SyncTarget)} is called for every SyncTarget object that wants to be
+ * included in the sync. If the addSync is called for a View or SurfaceView it needs to be called
+ * on the UI thread. When addToSync is called, it's guaranteed that any UI updates that were
+ * requested before addToSync but after the last frame drew, will be included in the sync.
+ * 3. {@link #markSyncReady(int)} should be called when all the {@link SyncTarget}s have been added
+ * to the SyncSet. Now the SyncSet is closed and no more SyncTargets can be added to it.
+ * 4. The SyncSet will gather the data for each SyncTarget using the steps described below. When
+ * all the SyncTargets have finished, the syncRequestComplete will be invoked and the transaction
+ * will either be applied or sent to the caller. In most cases, only the SurfaceSyncer should be
+ * handling the Transaction object directly. However, there are some cases where the framework
+ * needs to send the Transaction elsewhere, like in ViewRootImpl, so that option is provided.
+ *
+ * The following is what happens within the {@link SyncSet}
+ * 1. Each SyncableTarget will get a {@link SyncTarget#onReadyToSync} callback that contains
+ * a {@link SyncBufferCallback}.
+ * 2. Each {@link SyncTarget} needs to invoke {@link SyncBufferCallback#onBufferReady(Transaction)}.
+ * This makes sure the SyncSet knows when the SyncTarget is complete, allowing the SyncSet to get
+ * the Transaction that contains the buffer.
+ * 3. When the final SyncBufferCallback finishes for the SyncSet, the syncRequestComplete Consumer
+ * will be invoked with the transaction that contains all information requested in the sync. This
+ * could include buffers and geometry changes. The buffer update will include the UI changes that
+ * were requested for the View.
+ *
+ * @hide
+ */
+public class SurfaceSyncer {
+ private static final String TAG = "SurfaceSyncer";
+ private static final boolean DEBUG = false;
+
+ private static Supplier<Transaction> sTransactionFactory = Transaction::new;
+
+ private final Object mSyncSetLock = new Object();
+ @GuardedBy("mSyncSetLock")
+ private final SparseArray<SyncSet> mSyncSets = new SparseArray<>();
+ @GuardedBy("mSyncSetLock")
+ private int mIdCounter = 0;
+
+ /**
+ * @hide
+ */
+ public static void setTransactionFactory(Supplier<Transaction> transactionFactory) {
+ sTransactionFactory = transactionFactory;
+ }
+
+ /**
+ * Starts a sync and will automatically apply the final, merged transaction.
+ *
+ * @param onComplete The runnable that is invoked when the sync has completed. This will run on
+ * the same thread that the sync was started on.
+ * @return The syncId for the newly created sync.
+ * @see #setupSync(Consumer)
+ */
+ public int setupSync(@Nullable Runnable onComplete) {
+ Handler handler = new Handler(Looper.myLooper());
+ return setupSync(transaction -> {
+ transaction.apply();
+ handler.post(onComplete);
+ });
+ }
+
+ /**
+ * Starts a sync.
+ *
+ * @param syncRequestComplete The complete callback that contains the syncId and transaction
+ * with all the sync data merged.
+ * @return The syncId for the newly created sync.
+ * @hide
+ * @see #setupSync(Runnable)
+ */
+ public int setupSync(@NonNull Consumer<Transaction> syncRequestComplete) {
+ synchronized (mSyncSetLock) {
+ mIdCounter++;
+ if (DEBUG) {
+ Log.d(TAG, "setupSync " + mIdCounter);
+ }
+ SyncSet syncSet = new SyncSet(mIdCounter, syncRequestComplete);
+ mSyncSets.put(mIdCounter, syncSet);
+ return mIdCounter;
+ }
+ }
+
+ /**
+ * Mark the sync set as ready to complete. No more data can be added to the specified syncId.
+ * Once the sync set is marked as ready, it will be able to complete once all Syncables in the
+ * set have completed their sync
+ *
+ * @param syncId The syncId to mark as ready.
+ */
+ public void markSyncReady(int syncId) {
+ SyncSet syncSet;
+ synchronized (mSyncSetLock) {
+ syncSet = mSyncSets.get(syncId);
+ mSyncSets.remove(syncId);
+ }
+ if (syncSet == null) {
+ Log.e(TAG, "Failed to find syncSet for syncId=" + syncId);
+ return;
+ }
+ syncSet.markSyncReady();
+ }
+
+ /**
+ * Add a SurfaceView to a sync set. This is different than {@link #addToSync(int, View)} because
+ * it requires the caller to notify the start and finish drawing in order to sync.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param surfaceView The SurfaceView to add to the sync.
+ * @param frameCallbackConsumer The callback that's invoked to allow the caller to notify the
+ * Syncer when the SurfaceView has started drawing and finished.
+ *
+ * @return true if the SurfaceView was successfully added to the SyncSet, false otherwise.
+ */
+ @UiThread
+ public boolean addToSync(int syncId, SurfaceView surfaceView,
+ Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
+ return addToSync(syncId, new SurfaceViewSyncTarget(surfaceView, frameCallbackConsumer));
+ }
+
+ /**
+ * Add a View's rootView to a sync set.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param view The view where the root will be add to the sync set
+ *
+ * @return true if the View was successfully added to the SyncSet, false otherwise.
+ */
+ @UiThread
+ public boolean addToSync(int syncId, @NonNull View view) {
+ return addToSync(syncId, view.getViewRootImpl().mSyncTarget);
+ }
+
+ /**
+ * Add a {@link SyncTarget} to a sync set. The sync set will wait for all
+ * SyncableSurfaces to complete before notifying.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param syncTarget A SyncableSurface that implements how to handle syncing
+ * buffers.
+ *
+ * @return true if the SyncTarget was successfully added to the SyncSet, false otherwise.
+ */
+ public boolean addToSync(int syncId, @NonNull SyncTarget syncTarget) {
+ SyncSet syncSet = getAndValidateSyncSet(syncId);
+ if (syncSet == null) {
+ return false;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "addToSync id=" + syncId);
+ }
+ syncSet.addSyncableSurface(syncTarget);
+ return true;
+ }
+
+ /**
+ * Add a transaction to a specific sync so it can be merged along with the frames from the
+ * Syncables in the set. This is so the caller can add arbitrary transaction info that will be
+ * applied at the same time as the buffers
+ * @param syncId The syncId where the transaction will be merged to.
+ * @param t The transaction to merge in the sync set.
+ */
+ public void addTransactionToSync(int syncId, Transaction t) {
+ SyncSet syncSet = getAndValidateSyncSet(syncId);
+ if (syncSet != null) {
+ syncSet.addTransactionToSync(t);
+ }
+ }
+
+ private SyncSet getAndValidateSyncSet(int syncId) {
+ SyncSet syncSet;
+ synchronized (mSyncSetLock) {
+ syncSet = mSyncSets.get(syncId);
+ }
+ if (syncSet == null) {
+ Log.e(TAG, "Failed to find sync for id=" + syncId);
+ return null;
+ }
+ return syncSet;
+ }
+
+ /**
+ * A SyncTarget that can be added to a sync set.
+ */
+ public interface SyncTarget {
+ /**
+ * Called when the Syncable is ready to begin handing a sync request. When invoked, the
+ * implementor is required to call {@link SyncBufferCallback#onBufferReady(Transaction)}
+ * and {@link SyncBufferCallback#onBufferReady(Transaction)} in order for this Syncable
+ * to be marked as complete.
+ *
+ * @param syncBufferCallback A SyncBufferCallback that the caller must invoke onBufferReady
+ */
+ void onReadyToSync(SyncBufferCallback syncBufferCallback);
+ }
+
+ /**
+ * Interface so the SurfaceSyncer can know when it's safe to start and when everything has been
+ * completed. The caller should invoke the calls when the rendering has started and finished a
+ * frame.
+ */
+ public interface SyncBufferCallback {
+ /**
+ * Invoked when the transaction contains the buffer and is ready to sync.
+ *
+ * @param t The transaction that contains the buffer to be synced. This can be null if
+ * there's nothing to sync
+ */
+ void onBufferReady(@Nullable Transaction t);
+ }
+
+ /**
+ * Class that collects the {@link SyncTarget}s and notifies when all the surfaces have
+ * a frame ready.
+ */
+ private static class SyncSet {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final Set<Integer> mPendingSyncs = new HashSet<>();
+ @GuardedBy("mLock")
+ private final Transaction mTransaction = sTransactionFactory.get();
+ @GuardedBy("mLock")
+ private boolean mSyncReady;
+
+ private final int mSyncId;
+ private final Consumer<Transaction> mSyncRequestCompleteCallback;
+
+ private SyncSet(int syncId, Consumer<Transaction> syncRequestComplete) {
+ mSyncId = syncId;
+ mSyncRequestCompleteCallback = syncRequestComplete;
+ }
+
+ void addSyncableSurface(SyncTarget syncTarget) {
+ SyncBufferCallback syncBufferCallback = new SyncBufferCallback() {
+ @Override
+ public void onBufferReady(Transaction t) {
+ synchronized (mLock) {
+ if (t != null) {
+ mTransaction.merge(t);
+ }
+ mPendingSyncs.remove(hashCode());
+ checkIfSyncIsComplete();
+ }
+ }
+ };
+
+ synchronized (mLock) {
+ mPendingSyncs.add(syncBufferCallback.hashCode());
+ }
+ syncTarget.onReadyToSync(syncBufferCallback);
+ }
+
+ void markSyncReady() {
+ synchronized (mLock) {
+ mSyncReady = true;
+ checkIfSyncIsComplete();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void checkIfSyncIsComplete() {
+ if (!mSyncReady || !mPendingSyncs.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "Syncable is not complete. mSyncReady=" + mSyncReady
+ + " mPendingSyncs=" + mPendingSyncs.size());
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Successfully finished sync id=" + mSyncId);
+ }
+ mSyncRequestCompleteCallback.accept(mTransaction);
+ }
+
+ /**
+ * Add a Transaction to this sync set. This allows the caller to provide other info that
+ * should be synced with the buffers.
+ */
+ void addTransactionToSync(Transaction t) {
+ synchronized (mLock) {
+ mTransaction.merge(t);
+ }
+ }
+ }
+
+ /**
+ * Wrapper class to help synchronize SurfaceViews
+ */
+ private static class SurfaceViewSyncTarget implements SyncTarget {
+ private final SurfaceView mSurfaceView;
+ private final Consumer<SurfaceViewFrameCallback> mFrameCallbackConsumer;
+
+ SurfaceViewSyncTarget(SurfaceView surfaceView,
+ Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
+ mSurfaceView = surfaceView;
+ mFrameCallbackConsumer = frameCallbackConsumer;
+ }
+
+ @Override
+ public void onReadyToSync(SyncBufferCallback syncBufferCallback) {
+ Transaction mTransaction = sTransactionFactory.get();
+ mFrameCallbackConsumer.accept(new SurfaceViewFrameCallback() {
+ @Override
+ public void onFrameStarted() {
+ mSurfaceView.syncNextFrame(mTransaction);
+ }
+
+ @Override
+ public void onFrameComplete() {
+ syncBufferCallback.onBufferReady(mTransaction);
+ }
+ });
+ }
+ }
+
+ /**
+ * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
+ * implement onFrameStarted and onFrameComplete when trying to sync the SurfaceView. This is to
+ * ensure the sync knows when the frame is ready to add to the sync.
+ */
+ public interface SurfaceViewFrameCallback {
+ /**
+ * Called when the SurfaceView is going to render a frame
+ */
+ void onFrameStarted();
+
+ /**
+ * Called when the SurfaceView has finished rendering a frame.
+ */
+ void onFrameComplete();
+ }
+}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 346aa3336c12..35c959493f23 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -31,6 +31,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import java.util.ArrayList;
@@ -589,6 +590,56 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Adds a given {@code Rect} as a rect insets provider on the {@code receiverWindowContainer}.
+ * This will trigger a change of insets for all the children in the subtree of
+ * {@code receiverWindowContainer}.
+ *
+ * @param receiverWindowContainer the window container which the insets provider need to be
+ * added to
+ * @param insetsProviderFrame the frame that will be added as Insets provider
+ * @param insetsTypes types of insets the rect provides
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction addRectInsetsProvider(
+ @NonNull WindowContainerToken receiverWindowContainer,
+ @NonNull Rect insetsProviderFrame,
+ @InsetsState.InternalInsetsType int[] insetsTypes) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER)
+ .setContainer(receiverWindowContainer.asBinder())
+ .setInsetsProviderFrame(insetsProviderFrame)
+ .setInsetsTypes(insetsTypes)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Removes the insets provider for the given types from the
+ * {@code receiverWindowContainer}. This will trigger a change of insets for all the children
+ * in the subtree of {@code receiverWindowContainer}.
+ *
+ * @param receiverWindowContainer the window container which the insets-override-provider has
+ * to be removed from
+ * @param insetsTypes types of insets that have to be removed
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction removeInsetsProvider(
+ @NonNull WindowContainerToken receiverWindowContainer,
+ @InsetsState.InternalInsetsType int[] insetsTypes) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER)
+ .setContainer(receiverWindowContainer.asBinder())
+ .setInsetsTypes(insetsTypes)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
* When this {@link WindowContainerTransaction} failed to finish on the server side, it will
* trigger callback with this {@param errorCallbackToken}.
* @param errorCallbackToken client provided token that will be passed back as parameter in
@@ -993,6 +1044,8 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15;
+ public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
+ public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1012,6 +1065,10 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable
private IBinder mReparent;
+ private @InsetsState.InternalInsetsType int[] mInsetsTypes;
+
+ private Rect mInsetsProviderFrame;
+
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private boolean mToTop;
@@ -1130,6 +1187,8 @@ public final class WindowContainerTransaction implements Parcelable {
mType = copy.mType;
mContainer = copy.mContainer;
mReparent = copy.mReparent;
+ mInsetsTypes = copy.mInsetsTypes;
+ mInsetsProviderFrame = copy.mInsetsProviderFrame;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
@@ -1146,6 +1205,12 @@ public final class WindowContainerTransaction implements Parcelable {
mType = in.readInt();
mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder();
+ mInsetsTypes = in.createIntArray();
+ if (in.readInt() != 0) {
+ mInsetsProviderFrame = Rect.CREATOR.createFromParcel(in);
+ } else {
+ mInsetsProviderFrame = null;
+ }
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
mMoveAdjacentTogether = in.readBoolean();
@@ -1171,6 +1236,15 @@ public final class WindowContainerTransaction implements Parcelable {
return mReparent;
}
+ @Nullable
+ public @InsetsState.InternalInsetsType int[] getInsetsTypes() {
+ return mInsetsTypes;
+ }
+
+ public Rect getInsetsProviderFrame() {
+ return mInsetsProviderFrame;
+ }
+
@NonNull
public IBinder getContainer() {
return mContainer;
@@ -1276,6 +1350,13 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_START_SHORTCUT:
return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
+ "}";
+ case HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER:
+ return "{addRectInsetsProvider: container=" + mContainer
+ + " insetsProvidingFrame=" + mInsetsProviderFrame
+ + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
+ case HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER:
+ return "{removeLocalInsetsProvider: container=" + mContainer
+ + " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
@@ -1289,6 +1370,13 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeInt(mType);
dest.writeStrongBinder(mContainer);
dest.writeStrongBinder(mReparent);
+ dest.writeIntArray(mInsetsTypes);
+ if (mInsetsProviderFrame != null) {
+ dest.writeInt(1);
+ mInsetsProviderFrame.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
dest.writeBoolean(mMoveAdjacentTogether);
@@ -1328,6 +1416,10 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable
private IBinder mReparent;
+ private int[] mInsetsTypes;
+
+ private Rect mInsetsProviderFrame;
+
private boolean mToTop;
private boolean mReparentTopOnly;
@@ -1369,6 +1461,16 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setInsetsTypes(int[] insetsTypes) {
+ mInsetsTypes = insetsTypes;
+ return this;
+ }
+
+ Builder setInsetsProviderFrame(Rect insetsProviderFrame) {
+ mInsetsProviderFrame = insetsProviderFrame;
+ return this;
+ }
+
Builder setToTop(boolean toTop) {
mToTop = toTop;
return this;
@@ -1430,6 +1532,8 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mActivityTypes = mActivityTypes != null
? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
: null;
+ hierarchyOp.mInsetsTypes = mInsetsTypes;
+ hierarchyOp.mInsetsProviderFrame = mInsetsProviderFrame;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 0503c406c287..909e2770ddcc 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -54,7 +54,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private static final String TAG = "WindowOnBackDispatcher";
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
- .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -76,9 +76,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
/** Detaches the dispatcher instance from its window. */
public void detachFromWindow() {
+ clear();
mWindow = null;
mWindowSession = null;
- clear();
}
// TODO: Take an Executor for the callback to run on.
@@ -165,21 +165,32 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
} else {
int priority = mAllCallbacks.get(callback);
mWindowSession.setOnBackInvokedCallback(
- mWindow, new OnBackInvokedCallbackWrapper(callback, priority), priority);
+ mWindow, new OnBackInvokedCallbackWrapper(callback), priority);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
}
}
- private class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+ @Override
+ public OnBackInvokedCallback getTopCallback() {
+ if (mAllCallbacks.isEmpty()) {
+ return null;
+ }
+ for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ if (!callbacks.isEmpty()) {
+ return callbacks.get(callbacks.size() - 1);
+ }
+ }
+ return null;
+ }
+
+ private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
private final OnBackInvokedCallback mCallback;
- private final @Priority int mPriority;
- OnBackInvokedCallbackWrapper(
- @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
mCallback = callback;
- mPriority = priority;
}
@NonNull
@@ -188,18 +199,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
@Override
- public void onBackStarted() throws RemoteException {
+ public void onBackStarted() {
Handler.getMain().post(() -> mCallback.onBackStarted());
}
@Override
- public void onBackProgressed(BackEvent backEvent)
- throws RemoteException {
+ public void onBackProgressed(BackEvent backEvent) {
Handler.getMain().post(() -> mCallback.onBackProgressed(backEvent));
}
@Override
- public void onBackCancelled() throws RemoteException {
+ public void onBackCancelled() {
Handler.getMain().post(() -> mCallback.onBackCancelled());
}
@@ -209,20 +219,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
}
- @Override
- public OnBackInvokedCallback getTopCallback() {
- if (mAllCallbacks.isEmpty()) {
- return null;
- }
- for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- if (!callbacks.isEmpty()) {
- return callbacks.get(callbacks.size() - 1);
- }
- }
- return null;
- }
-
/**
* Returns if the legacy back behavior should be used.
*
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 6d4b8c5ea1ad..b1e7d15cbf4a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -20,13 +20,21 @@ import static android.system.OsConstants.O_CLOEXEC;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ProcessInfo;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
+import android.os.Build;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace;
import android.provider.DeviceConfig;
@@ -34,6 +42,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.net.NetworkUtilsInternal;
import dalvik.annotation.optimization.CriticalNative;
@@ -125,6 +134,7 @@ public final class Zygote {
public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
public static final int MEMORY_TAG_LEVEL_NONE = 0;
+
/**
* Enable pointer tagging in this process.
* Tags are checked during memory deallocation, but not on access.
@@ -170,10 +180,8 @@ public final class Zygote {
*/
public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
- /**
- * Enable automatic zero-initialization of native heap memory allocations.
- */
- public static final int NATIVE_HEAP_ZERO_INIT = 1 << 23;
+ /** Enable automatic zero-initialization of native heap memory allocations. */
+ public static final int NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23;
/**
* Enable profiling from system services. This loads profiling related plugins in ART.
@@ -1170,4 +1178,251 @@ public final class Zygote {
* we failed to determine the level.
*/
public static native int nativeCurrentTaggingLevel();
+
+ /**
+ * Native heap allocations will now have a non-zero tag in the most significant byte.
+ *
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
+ /**
+ * Native heap allocations in AppZygote process and its descendants will now have a non-zero tag
+ * in the most significant byte.
+ *
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long NATIVE_HEAP_POINTER_TAGGING_SECONDARY_ZYGOTE = 207557677;
+
+ /**
+ * Enable asynchronous (ASYNC) memory tag checking in this process. This flag will only have an
+ * effect on hardware supporting the ARM Memory Tagging Extension (MTE).
+ */
+ @ChangeId @Disabled
+ private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id.
+
+ /**
+ * Enable synchronous (SYNC) memory tag checking in this process. This flag will only have an
+ * effect on hardware supporting the ARM Memory Tagging Extension (MTE). If both
+ * NATIVE_MEMTAG_ASYNC and this option is selected, this option takes preference and MTE is
+ * enabled in SYNC mode.
+ */
+ @ChangeId @Disabled
+ private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
+
+ /** Enable automatic zero-initialization of native heap memory allocations. */
+ @ChangeId @Disabled
+ private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id.
+
+ /**
+ * Enable sampled memory bug detection in the app.
+ *
+ * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
+ */
+ @ChangeId @Disabled private static final long GWP_ASAN = 135634846; // This is a bug id.
+
+ private static int memtagModeToZygoteMemtagLevel(int memtagMode) {
+ switch (memtagMode) {
+ case ApplicationInfo.MEMTAG_ASYNC:
+ return MEMORY_TAG_LEVEL_ASYNC;
+ case ApplicationInfo.MEMTAG_SYNC:
+ return MEMORY_TAG_LEVEL_SYNC;
+ default:
+ return MEMORY_TAG_LEVEL_NONE;
+ }
+ }
+
+ private static boolean isCompatChangeEnabled(
+ long change,
+ @NonNull ApplicationInfo info,
+ @Nullable IPlatformCompat platformCompat,
+ int enabledAfter) {
+ try {
+ if (platformCompat != null) return platformCompat.isChangeEnabled(change, info);
+ } catch (RemoteException ignore) {
+ }
+ return enabledAfter > 0 && info.targetSdkVersion > enabledAfter;
+ }
+
+ // Returns the requested memory tagging level.
+ private static int getRequestedMemtagLevel(
+ @NonNull ApplicationInfo info,
+ @Nullable ProcessInfo processInfo,
+ @Nullable IPlatformCompat platformCompat) {
+ // Look at the process attribute first.
+ if (processInfo != null && processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
+ return memtagModeToZygoteMemtagLevel(processInfo.memtagMode);
+ }
+
+ // Then at the application attribute.
+ if (info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) {
+ return memtagModeToZygoteMemtagLevel(info.getMemtagMode());
+ }
+
+ if (isCompatChangeEnabled(NATIVE_MEMTAG_SYNC, info, platformCompat, 0)) {
+ return MEMORY_TAG_LEVEL_SYNC;
+ }
+
+ if (isCompatChangeEnabled(NATIVE_MEMTAG_ASYNC, info, platformCompat, 0)) {
+ return MEMORY_TAG_LEVEL_ASYNC;
+ }
+
+ // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute.
+ if (!info.allowsNativeHeapPointerTagging()) {
+ return MEMORY_TAG_LEVEL_NONE;
+ }
+
+ String defaultLevel = SystemProperties.get("persist.arm64.memtag.app_default");
+ if ("sync".equals(defaultLevel)) {
+ return MEMORY_TAG_LEVEL_SYNC;
+ } else if ("async".equals(defaultLevel)) {
+ return MEMORY_TAG_LEVEL_ASYNC;
+ }
+
+ // Check to see that the compat feature for TBI is enabled.
+ if (isCompatChangeEnabled(
+ NATIVE_HEAP_POINTER_TAGGING, info, platformCompat, Build.VERSION_CODES.Q)) {
+ return MEMORY_TAG_LEVEL_TBI;
+ }
+
+ return MEMORY_TAG_LEVEL_NONE;
+ }
+
+ private static int decideTaggingLevel(
+ @NonNull ApplicationInfo info,
+ @Nullable ProcessInfo processInfo,
+ @Nullable IPlatformCompat platformCompat) {
+ // Get the desired tagging level (app manifest + compat features).
+ int level = getRequestedMemtagLevel(info, processInfo, platformCompat);
+
+ // Take into account the hardware capabilities.
+ if (nativeSupportsMemoryTagging()) {
+ // MTE devices can not do TBI, because the Zygote process already has live MTE
+ // allocations. Downgrade TBI to NONE.
+ if (level == MEMORY_TAG_LEVEL_TBI) {
+ level = MEMORY_TAG_LEVEL_NONE;
+ }
+ } else if (nativeSupportsTaggedPointers()) {
+ // TBI-but-not-MTE devices downgrade MTE modes to TBI.
+ // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with
+ // the "fake" pointer tagging (TBI).
+ if (level == MEMORY_TAG_LEVEL_ASYNC || level == MEMORY_TAG_LEVEL_SYNC) {
+ level = MEMORY_TAG_LEVEL_TBI;
+ }
+ } else {
+ // Otherwise disable all tagging.
+ level = MEMORY_TAG_LEVEL_NONE;
+ }
+
+ return level;
+ }
+
+ private static int decideGwpAsanLevel(
+ @NonNull ApplicationInfo info,
+ @Nullable ProcessInfo processInfo,
+ @Nullable IPlatformCompat platformCompat) {
+ // Look at the process attribute first.
+ if (processInfo != null && processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) {
+ return processInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_ALWAYS
+ ? GWP_ASAN_LEVEL_ALWAYS
+ : GWP_ASAN_LEVEL_NEVER;
+ }
+ // Then at the application attribute.
+ if (info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) {
+ return info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS
+ ? GWP_ASAN_LEVEL_ALWAYS
+ : GWP_ASAN_LEVEL_NEVER;
+ }
+ // If the app does not specify gwpAsanMode, the default behavior is lottery among the
+ // system apps, and disabled for user apps, unless overwritten by the compat feature.
+ if (isCompatChangeEnabled(GWP_ASAN, info, platformCompat, 0)) {
+ return GWP_ASAN_LEVEL_ALWAYS;
+ }
+ if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return GWP_ASAN_LEVEL_LOTTERY;
+ }
+ return GWP_ASAN_LEVEL_NEVER;
+ }
+
+ private static boolean enableNativeHeapZeroInit(
+ @NonNull ApplicationInfo info,
+ @Nullable ProcessInfo processInfo,
+ @Nullable IPlatformCompat platformCompat) {
+ // Look at the process attribute first.
+ if (processInfo != null
+ && processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) {
+ return processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED;
+ }
+ // Then at the application attribute.
+ if (info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) {
+ return info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED;
+ }
+ // Compat feature last.
+ if (isCompatChangeEnabled(NATIVE_HEAP_ZERO_INIT, info, platformCompat, 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns Zygote runtimeFlags for memory safety features (MTE, GWP-ASan, nativeHeadZeroInit)
+ * for a given app.
+ */
+ public static int getMemorySafetyRuntimeFlags(
+ @NonNull ApplicationInfo info,
+ @Nullable ProcessInfo processInfo,
+ @Nullable String instructionSet,
+ @Nullable IPlatformCompat platformCompat) {
+ int runtimeFlags = decideGwpAsanLevel(info, processInfo, platformCompat);
+ // If instructionSet is non-null, this indicates that the system_server is spawning a
+ // process with an ISA that may be different from its own. System (kernel and hardware)
+ // compatibility for these features is checked in the decideTaggingLevel in the
+ // system_server process (not the child process). As both MTE and TBI are only supported
+ // in aarch64, we can simply ensure that the new process is also aarch64. This prevents
+ // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should
+ // enable some tagging variant. Theoretically, a 32-bit system server could exist that
+ // spawns 64-bit processes, in which case the new process won't get any tagging. This is
+ // fine as we haven't seen this configuration in practice, and we can reasonable assume
+ // that if tagging is desired, the system server will be 64-bit.
+ if (instructionSet == null || instructionSet.equals("arm64")) {
+ runtimeFlags |= decideTaggingLevel(info, processInfo, platformCompat);
+ }
+ if (enableNativeHeapZeroInit(info, processInfo, platformCompat)) {
+ runtimeFlags |= NATIVE_HEAP_ZERO_INIT_ENABLED;
+ }
+ return runtimeFlags;
+ }
+
+ /**
+ * Returns Zygote runtimeFlags for memory safety features (MTE, GWP-ASan, nativeHeadZeroInit)
+ * for a secondary zygote (AppZygote or WebViewZygote).
+ */
+ public static int getMemorySafetyRuntimeFlagsForSecondaryZygote(
+ @NonNull ApplicationInfo info, @Nullable ProcessInfo processInfo) {
+ final IPlatformCompat platformCompat =
+ IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ int runtimeFlags =
+ getMemorySafetyRuntimeFlags(
+ info, processInfo, null /*instructionSet*/, platformCompat);
+
+ // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
+ if ((runtimeFlags & MEMORY_TAG_LEVEL_MASK) == MEMORY_TAG_LEVEL_TBI
+ && isCompatChangeEnabled(
+ NATIVE_HEAP_POINTER_TAGGING_SECONDARY_ZYGOTE,
+ info,
+ platformCompat,
+ Build.VERSION_CODES.S)) {
+ // Reset memory tag level to NONE.
+ runtimeFlags &= ~MEMORY_TAG_LEVEL_MASK;
+ runtimeFlags |= MEMORY_TAG_LEVEL_NONE;
+ }
+ return runtimeFlags;
+ }
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 099d1fc933d2..d629d66d1c31 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -159,7 +159,7 @@ oneway interface IStatusBar
/**
* Used to notify the authentication dialog that a biometric has been authenticated.
*/
- void onBiometricAuthenticated();
+ void onBiometricAuthenticated(int modality);
/**
* Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc.
*/
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index dcc1a7626a1b..9163b6d6215e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -125,7 +125,7 @@ interface IStatusBarService
int multiSensorConfig);
// Used to notify the authentication dialog that a biometric has been authenticated
- void onBiometricAuthenticated();
+ void onBiometricAuthenticated(int modality);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(int modality, String message);
// Used to show an error - the dialog will dismiss after a certain amount of time
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 93864fa2e05d..b1846d2402dd 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -671,6 +671,7 @@ public class SystemConfig {
readPermissions(parser, Environment.buildPath(f, "etc", "permissions"),
apexPermissionFlag);
}
+ pruneVendorApexPrivappAllowlists();
}
@VisibleForTesting
@@ -1197,7 +1198,8 @@ public class SystemConfig {
readPrivAppPermissions(parser, mSystemExtPrivAppPermissions,
mSystemExtPrivAppDenyPermissions);
} else if (apex) {
- readApexPrivAppPermissions(parser, permFile);
+ readApexPrivAppPermissions(parser, permFile,
+ Environment.getApexDirectory().toPath());
} else {
readPrivAppPermissions(parser, mPrivAppPermissions,
mPrivAppDenyPermissions);
@@ -1547,6 +1549,21 @@ public class SystemConfig {
}
}
+ /**
+ * Prunes out any privileged permission allowlists bundled in vendor apexes.
+ */
+ @VisibleForTesting
+ public void pruneVendorApexPrivappAllowlists() {
+ for (String moduleName: mAllowedVendorApexes.keySet()) {
+ if (mApexPrivAppPermissions.containsKey(moduleName)
+ || mApexPrivAppDenyPermissions.containsKey(moduleName)) {
+ Slog.w(TAG, moduleName + " is a vendor apex, ignore its priv-app allowlist");
+ mApexPrivAppPermissions.remove(moduleName);
+ mApexPrivAppDenyPermissions.remove(moduleName);
+ }
+ }
+ }
+
private void readInstallInUserType(XmlPullParser parser,
Map<String, Set<String>> doInstallMap,
Map<String, Set<String>> nonInstallMap)
@@ -1757,8 +1774,7 @@ public class SystemConfig {
/**
* Returns the module name for a file in the apex module's partition.
*/
- private String getApexModuleNameFromFilePath(Path path) {
- final Path apexDirectoryPath = Environment.getApexDirectory().toPath();
+ private String getApexModuleNameFromFilePath(Path path, Path apexDirectoryPath) {
if (!path.startsWith(apexDirectoryPath)) {
throw new IllegalArgumentException("File " + path + " is not part of an APEX.");
}
@@ -1770,9 +1786,14 @@ public class SystemConfig {
return path.getName(apexDirectoryPath.getNameCount()).toString();
}
- private void readApexPrivAppPermissions(XmlPullParser parser, File permFile)
- throws IOException, XmlPullParserException {
- final String moduleName = getApexModuleNameFromFilePath(permFile.toPath());
+ /**
+ * Reads the contents of the privileged permission allowlist stored inside an APEX.
+ */
+ @VisibleForTesting
+ public void readApexPrivAppPermissions(XmlPullParser parser, File permFile,
+ Path apexDirectoryPath) throws IOException, XmlPullParserException {
+ final String moduleName =
+ getApexModuleNameFromFilePath(permFile.toPath(), apexDirectoryPath);
final ArrayMap<String, ArraySet<String>> privAppPermissions;
if (mApexPrivAppPermissions.containsKey(moduleName)) {
privAppPermissions = mApexPrivAppPermissions.get(moduleName);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index dc55c0512941..e7817606aae9 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -829,13 +829,6 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz)
}
static jint
-android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
-{
- return (jint)deviceTypesToBitMask(
- AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(stream)));
-}
-
-static jint
android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz)
{
return (jint) AudioSystem::getPrimaryOutputSamplingRate();
@@ -2712,10 +2705,10 @@ static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *
return AUDIO_JAVA_SUCCESS;
}
-static jint
-android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
- jobject jaa, jobjectArray jDeviceArray)
-{
+static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
+ jobject jaa,
+ jobjectArray jDeviceArray,
+ jboolean forVolume) {
const jsize maxResultSize = env->GetArrayLength(jDeviceArray);
// the JNI is always expected to provide us with an array capable of holding enough
// devices i.e. the most we ever route a track to. This is preferred over receiving an ArrayList
@@ -2734,7 +2727,7 @@ android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
AudioDeviceTypeAddrVector devices;
jStatus = check_AudioSystem_Command(
- AudioSystem::getDevicesForAttributes(*(paa.get()), &devices));
+ AudioSystem::getDevicesForAttributes(*(paa.get()), &devices, forVolume));
if (jStatus != NO_ERROR) {
return jStatus;
}
@@ -2959,7 +2952,6 @@ static const JNINativeMethod gMethods[] =
{"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono},
{"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance},
{"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance},
- {"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
{"getPrimaryOutputSamplingRate", "()I",
(void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
{"getPrimaryOutputFrameCount", "()I",
@@ -3045,7 +3037,7 @@ static const JNINativeMethod gMethods[] =
{"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
(void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
{"getDevicesForAttributes",
- "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
+ "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;Z)I",
(void *)android_media_AudioSystem_getDevicesForAttributes},
{"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
(void *)android_media_AudioSystem_setUserIdDeviceAffinities},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 095641864030..68025a8ba0e4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -62,6 +62,7 @@
namespace android {
+using gui::CaptureArgs;
using gui::FocusRequest;
static void doThrowNPE(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 597167026d19..5b7092cabfcb 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -346,7 +346,7 @@ enum RuntimeFlags : uint32_t {
GWP_ASAN_LEVEL_NEVER = 0 << 21,
GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
- NATIVE_HEAP_ZERO_INIT = 1 << 23,
+ NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23,
PROFILEABLE = 1 << 24,
};
@@ -1709,13 +1709,13 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// would be nice to have them for apps, we will have to wait until they are
// proven out, have more efficient hardware, and/or apply them only to new
// applications.
- if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
+ if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT_ENABLED)) {
mallopt(M_BIONIC_ZERO_INIT, 0);
}
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
// runtime.
- runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
+ runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT_ENABLED;
bool forceEnableGwpAsan = false;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 97870a16da2b..9359528b613b 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -128,7 +128,7 @@ message SensorPrivacyToggleSourceProto {
DIALOG = 3;
SHELL = 4;
OTHER = 5;
- SAFETY_HUB = 6;
+ SAFETY_CENTER = 6;
}
// Source for which sensor privacy was toggled.
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
index fc9da90ccd59..ac9e3e001d50 100644
--- a/core/proto/android/server/biometrics.proto
+++ b/core/proto/android/server/biometrics.proto
@@ -91,23 +91,9 @@ message BiometricServiceStateProto {
STATE_CLIENT_DIED_CANCELLING = 10;
}
- enum MultiSensorState {
- // Initializing or not yet started.
- MULTI_SENSOR_STATE_UNKNOWN = 0;
- // Sensors are in the process of being transitioned and there is no active sensor.
- MULTI_SENSOR_STATE_SWITCHING = 1;
- // Face sensor is being used as the primary input.
- MULTI_SENSOR_STATE_FACE_SCANNING = 2;
- // Fingerprint sensor is being used as the primary input.
- MULTI_SENSOR_STATE_FP_SCANNING = 3;
- }
-
repeated SensorServiceStateProto sensor_service_states = 1;
optional AuthSessionState auth_session_state = 2;
-
- // Additional session state information, when the device has multiple sensors.
- optional MultiSensorState auth_session_multi_sensor_state = 3;
}
// Overall state for an instance of a <Biometric>Service, for example FingerprintService or
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index e8f7b9343746..3929027a4e94 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -446,6 +446,7 @@ message WindowStateProto {
optional bool has_compat_scale = 43;
optional float global_scale = 44;
repeated .android.graphics.RectProto keep_clear_areas = 45;
+ repeated .android.graphics.RectProto unrestricted_keep_clear_areas = 46;
}
message IdentifierProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 58a3bb4818df..d7744e377f12 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1903,14 +1903,16 @@
to improve wifi performance.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- Allows applications to get notified when a Wi-Fi interface request cannot
be satisfied without tearing down one or more other interfaces, and provide a decision
whether to approve the request or reject it.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_WIFI_INTERFACES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
<p>Only granted to applications that are currently bound by the
@@ -1948,7 +1950,8 @@
modifications.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- @deprecated Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
@@ -6923,6 +6926,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.BinaryTransparencyService$UpdateMeasurementsJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
android:exported="false">
<intent-filter>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 8f2d6c3e02f4..dff7751b1287 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -185,6 +185,12 @@
<item>@string/app_info</item>
</string-array>
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner Wi-Fi
+ permissions. The digest should be computed over the DER encoding of the trusted certificate
+ using the SHA-256 digest algorithm. -->
+ <string-array name="wifi_known_signers">
+ </string-array>
+
<!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
eUICC, then the value of this array should be:
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5ac30de631c5..0763ddb0be94 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1207,13 +1207,13 @@
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
- <integer name="config_criticalBatteryWarningLevel">5</integer>
+ <integer name="config_criticalBatteryWarningLevel">10</integer>
<!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
<integer name="config_shutdownBatteryTemperature">680</integer>
<!-- Display low battery warning when battery level dips to this value -->
- <integer name="config_lowBatteryWarningLevel">15</integer>
+ <integer name="config_lowBatteryWarningLevel">20</integer>
<!-- The default suggested battery % at which we enable battery saver automatically. -->
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
@@ -2113,7 +2113,7 @@
TODO(b/189347385) make this a @SystemAPI -->
<string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
<!-- The name of the package that will hold the device management role -->
- <string name="config_deviceManager" translatable="false"></string>
+ <string name="config_devicePolicyManagement" translatable="false"></string>
<!-- The name of the package that will hold the app protection service role. -->
<string name="config_systemAppProtectionService" translatable="false"></string>
<!-- The name of the package that will hold the system calendar sync manager role. -->
@@ -2122,7 +2122,7 @@
<string name="config_defaultAutomotiveNavigation" translatable="false"></string>
<!-- The name of the package that will handle updating the device management role. -->
- <string name="config_deviceManagerUpdater" translatable="false"></string>
+ <string name="config_devicePolicyManagementUpdater" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
@@ -4130,9 +4130,9 @@
This service must be trusted, as it can be activated without explicit consent of the user.
If no service with the specified name exists on the device, cloudsearch will be disabled.
Example: "com.android.intelligence/.CloudSearchService"
- config_defaultCloudSearchService is for the single provider case.
+ config_defaultCloudSearchServices is for the multiple provider case.
-->
- <string name="config_defaultCloudSearchService" translatable="false"></string>
+ <string-array name="config_defaultCloudSearchServices"></string-array>
<!-- The package name for the system's translation service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index bccd2b6529ac..29eb0c0365b3 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -62,8 +62,9 @@
<!-- View id for the action of text editor inside of an extracted text
{@link InputMethodService#onCreateExtractTextView IME extract view}. -->
<item type="id" name="inputExtractAction" />
- <!-- View id for the accessories of text editor inside of an extracted text
- {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+ <!-- View id for the accessories (such as the extracted input action button) of text editor
+ inside of an extracted text {@link InputMethodService#onCreateExtractTextView IME extract
+ view}. This layout must contain the {@link #inputExtractAction}. -->
<item type="id" name="inputExtractAccessories" />
<item type="id" name="selectAll" />
<item type="id" name="cut" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3beb4b2f7ee3..3467e1b40470 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3301,7 +3301,7 @@
<!-- @hide @SystemApi -->
<public name="config_systemSupervision" />
<!-- @hide @SystemApi -->
- <public name="config_deviceManager" />
+ <public name="config_devicePolicyManagement" />
<!-- @hide @SystemApi -->
<public name="config_systemAppProtectionService" />
<!-- @hide @SystemApi @TestApi -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e41aa45e0a44..e64756119478 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -197,7 +197,7 @@
<string name="ThreeWCMmi">Three way calling</string>
<string name="RuacMmi">Rejection of undesired annoying calls</string>
<string name="CndMmi">Calling number delivery</string>
- <string name="DndMmi" translatable="false">Priority mode</string>
+ <string name="DndMmi">Do not disturb</string>
<!-- Displayed to confirm to the user that caller ID will be restricted on the next call as usual. -->
<string name="CLIRDefaultOnNextCallOn">Caller ID defaults to restricted. Next call: Restricted</string>
@@ -2036,9 +2036,9 @@
<string name="permdesc_bindCarrierServices">Allows the holder to bind to carrier services. Should never be needed for normal apps.</string>
<!-- Title of an application permission, for applications that wish to access notification policy. -->
- <string name="permlab_access_notification_policy" translatable="false">access Priority mode</string>
+ <string name="permlab_access_notification_policy">access Do Not Disturb</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_access_notification_policy" translatable="false">Allows the app to read and write Priority mode configuration.</string>
+ <string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_startViewPermissionUsage">start view permission usage</string>
@@ -5302,7 +5302,7 @@
<string name="zen_mode_forever">Until you turn off</string>
<!-- Zen mode condition: no exit criteria, includes the name of the feature for emphasis. [CHAR LIMIT=NONE] -->
- <string name="zen_mode_forever_dnd" translatable="false">Until you turn off Priority mode</string>
+ <string name="zen_mode_forever_dnd">Until you turn off Do Not Disturb</string>
<!-- Zen mode active automatic rule name separator. [CHAR LIMIT=NONE] -->
<string name="zen_mode_rule_name_combination"><xliff:g id="first" example="Weeknights">%1$s</xliff:g> / <xliff:g id="rest" example="Meetings">%2$s</xliff:g></string>
@@ -5311,7 +5311,7 @@
<string name="toolbar_collapse_description">Collapse</string>
<!-- Zen mode - feature name. [CHAR LIMIT=40] -->
- <string name="zen_mode_feature_name" translatable="false">Priority mode</string>
+ <string name="zen_mode_feature_name">Do not disturb</string>
<!-- Zen mode - downtime legacy feature name. [CHAR LIMIT=40] -->
<string name="zen_mode_downtime_feature_name">Downtime</string>
@@ -5745,14 +5745,14 @@
<!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
<string name="notification_channel_system_changes">System changes</string>
- <!-- Title for the notification channel notifying user of priority mode system changes (i.e. Priority mode has changed). [CHAR LIMIT=NONE] -->
- <string name="notification_channel_do_not_disturb" translatable="false">Priority mode</string>
- <!-- Title of notification indicating Priority mode visual interruption settings have changed when upgrading to P -->
- <string name="zen_upgrade_notification_visd_title" translatable="false">New: Priority mode is hiding notifications</string>
+ <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_do_not_disturb">Do Not Disturb</string>
+ <!-- Title of notification indicating do not disturb visual interruption settings have changed when upgrading to P -->
+ <string name="zen_upgrade_notification_visd_title">New: Do Not Disturb is hiding notifications</string>
<!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
<string name="zen_upgrade_notification_visd_content">Tap to learn more and change.</string>
- <!-- Title of notification indicating priority mode settings have changed when upgrading to P -->
- <string name="zen_upgrade_notification_title" translatable="false">Priority mode has changed</string>
+ <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
+ <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
<!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
<string name="zen_upgrade_notification_content">Tap to check what\'s blocked.</string>
@@ -5793,7 +5793,7 @@
<!-- Label of notification action button to learn more about the enhanced notifications [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_learn_more_action">Learn more</string>
<!-- Content of notification learn more dialog about the enhanced notifications [CHAR LIMIT=NONE] -->
- <string name="nas_upgrade_notification_learn_more_content" translatable="false">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Priority mode.</string>
+ <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Do Not Disturb.</string>
<!-- Dynamic mode battery saver strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e543034b6974..dae746b4bb7c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3675,7 +3675,7 @@
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
- <java-symbol type="string" name="config_defaultCloudSearchService" />
+ <java-symbol type="array" name="config_defaultCloudSearchServices" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />
@@ -4726,7 +4726,7 @@
<java-symbol type="bool" name="config_enableSafetyCenter" />
- <java-symbol type="string" name="config_deviceManagerUpdater" />
+ <java-symbol type="string" name="config_devicePolicyManagementUpdater" />
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2d2c03bf2215..3c64cf55eb50 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -20,13 +20,6 @@ This XML file declares which signature|privileged permissions should be granted
applications that come with the platform
-->
<permissions>
- <privapp-permissions package="android.ext.services">
- <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
- <permission name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
- <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
- <permission name="android.permission.INTERACT_ACROSS_USERS" />
- </privapp-permissions>
-
<!-- Needed for Build.getSerial(), which is used to send a unique number for serial, per HUIG. -->
<privapp-permissions package="android.car.usb.handler">
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 33b09b8831ce..55f205bb14a6 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -690,6 +690,14 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
}
}
+ /**
+ * Gets the total duration of the animation
+ * @hide
+ */
+ public long getTotalDuration() {
+ return mAnimatorSet.getTotalDuration();
+ }
+
private static class AnimatedVectorDrawableState extends ConstantState {
@Config int mChangingConfigurations;
VectorDrawable mVectorDrawable;
@@ -1074,6 +1082,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
boolean isInfinite();
void pause();
void resume();
+ long getTotalDuration();
}
private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
@@ -1085,6 +1094,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// setup by init().
private ArrayList<AnimatorListener> mListenerArray = null;
private boolean mIsInfinite = false;
+ private long mTotalDuration;
VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1100,7 +1110,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// Keep a deep copy of the set, such that set can be still be constantly representing
// the static content from XML file.
mSet = set.clone();
- mIsInfinite = mSet.getTotalDuration() == Animator.DURATION_INFINITE;
+ mTotalDuration = mSet.getTotalDuration();
+ mIsInfinite = mTotalDuration == Animator.DURATION_INFINITE;
// If there are listeners added before calling init(), now they should be setup.
if (mListenerArray != null && !mListenerArray.isEmpty()) {
@@ -1219,6 +1230,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private void invalidateOwningView() {
mDrawable.invalidateSelf();
}
+
+ @Override
+ public long getTotalDuration() {
+ return mTotalDuration;
+ }
}
/**
@@ -1249,6 +1265,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private int mLastListenerId = 0;
private final IntArray mPendingAnimationActions = new IntArray();
private final AnimatedVectorDrawable mDrawable;
+ private long mTotalDuration;
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1270,7 +1287,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
.getNativeTree();
nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
mInitialized = true;
- mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
+ mTotalDuration = set.getTotalDuration();
+ mIsInfinite = mTotalDuration == Animator.DURATION_INFINITE;
// Check reversible.
mIsReversible = true;
@@ -1796,6 +1814,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
}
mPendingAnimationActions.clear();
}
+
+ @Override
+ public long getTotalDuration() {
+ return mTotalDuration;
+ }
}
private static native long nCreateAnimatorSet();
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 8c3fa441cbb0..7fd2201e7108 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -424,6 +424,17 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
System.arraycopy(mDurations, 0, newDurations, 0, oldSize);
mDurations = newDurations;
}
+
+ public long getTotalDuration() {
+ if (mDurations != null) {
+ int total = 0;
+ for (int dur : mDurations) {
+ total += dur;
+ }
+ return total;
+ }
+ return 0;
+ }
}
@Override
@@ -435,6 +446,14 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
}
}
+ /**
+ * Gets the total duration of the animation
+ * @hide
+ */
+ public long getTotalDuration() {
+ return mAnimationState.getTotalDuration();
+ }
+
private AnimationDrawable(AnimationState state, Resources res) {
final AnimationState as = new AnimationState(state, this, res);
setConstantState(as);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 2e85b304ec47..31dd10a8ed53 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -66,6 +66,7 @@ import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
+import java.security.spec.NamedParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.ArrayList;
import java.util.Arrays;
@@ -119,36 +120,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private static final int RSA_MIN_KEY_SIZE = 512;
private static final int RSA_MAX_KEY_SIZE = 8192;
- private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
+ private static final Map<String, Integer> SUPPORTED_EC_CURVE_NAME_TO_SIZE =
new HashMap<String, Integer>();
- private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
- private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>();
+ private static final List<String> SUPPORTED_EC_CURVE_NAMES = new ArrayList<String>();
+ private static final List<Integer> SUPPORTED_EC_CURVE_SIZES = new ArrayList<Integer>();
+ private static final String CURVE_X_25519 = NamedParameterSpec.X25519.getName();
+ private static final String CURVE_ED_25519 = NamedParameterSpec.ED25519.getName();
+
static {
// Aliases for NIST P-224
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-224", 224);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
// Aliases for NIST P-256
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-256", 256);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+ // Aliases for Curve 25519
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_X_25519.toLowerCase(Locale.US), 256);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_ED_25519.toLowerCase(Locale.US), 256);
// Aliases for NIST P-384
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-384", 384);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
// Aliases for NIST P-521
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-521", 521);
+ SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
- SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
- Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
+ SUPPORTED_EC_CURVE_NAMES.addAll(SUPPORTED_EC_CURVE_NAME_TO_SIZE.keySet());
+ Collections.sort(SUPPORTED_EC_CURVE_NAMES);
- SUPPORTED_EC_NIST_CURVE_SIZES.addAll(
- new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values()));
- Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES);
+ SUPPORTED_EC_CURVE_SIZES.addAll(
+ new HashSet<Integer>(SUPPORTED_EC_CURVE_NAME_TO_SIZE.values()));
+ Collections.sort(SUPPORTED_EC_CURVE_SIZES);
}
private final int mOriginalKeymasterAlgorithm;
@@ -164,6 +171,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private int mKeySizeBits;
private SecureRandom mRng;
private KeyDescriptor mAttestKeyDescriptor;
+ private String mEcCurveName;
private int[] mKeymasterPurposes;
private int[] mKeymasterBlockModes;
@@ -177,12 +185,15 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mOriginalKeymasterAlgorithm = keymasterAlgorithm;
}
- private @EcCurve int keySize2EcCurve(int keySizeBits)
+ private static @EcCurve int keySizeAndNameToEcCurve(int keySizeBits, String ecCurveName)
throws InvalidAlgorithmParameterException {
switch (keySizeBits) {
case 224:
return EcCurve.P_224;
case 256:
+ if (isCurve25519(ecCurveName)) {
+ return EcCurve.CURVE_25519;
+ }
return EcCurve.P_256;
case 384:
return EcCurve.P_384;
@@ -247,7 +258,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeySizeBits == -1) {
mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
}
- checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
+ checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked(),
+ mEcCurveName);
if (spec.getKeystoreAlias() == null) {
throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
@@ -299,6 +311,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec);
checkAttestKeyPurpose(spec);
+ checkCorrectKeyPurposeForCurve(spec);
success = true;
} finally {
@@ -317,6 +330,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
}
+ private void checkCorrectKeyPurposeForCurve(KeyGenParameterSpec spec)
+ throws InvalidAlgorithmParameterException {
+ // Validate the key usage purposes against the curve. x25519 should be
+ // key exchange only, ed25519 signing and attesting.
+
+ if (!isCurve25519(mEcCurveName)) {
+ return;
+ }
+
+ if (mEcCurveName.equalsIgnoreCase(CURVE_X_25519)
+ && spec.getPurposes() != KeyProperties.PURPOSE_AGREE_KEY) {
+ throw new InvalidAlgorithmParameterException(
+ "x25519 may only be used for key agreement.");
+ } else if (mEcCurveName.equalsIgnoreCase(CURVE_ED_25519)
+ && !hasOnlyAllowedPurposeForEd25519(spec.getPurposes())) {
+ throw new InvalidAlgorithmParameterException(
+ "ed25519 may not be used for key agreement.");
+ }
+ }
+
+ private static boolean isCurve25519(String ecCurveName) {
+ if (ecCurveName == null) {
+ return false;
+ }
+ return ecCurveName.equalsIgnoreCase(CURVE_X_25519)
+ || ecCurveName.equalsIgnoreCase(CURVE_ED_25519);
+ }
+
+ private static boolean hasOnlyAllowedPurposeForEd25519(@KeyProperties.PurposeEnum int purpose) {
+ final int allowedPurposes = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ATTEST_KEY;
+ boolean hasAllowedPurpose = (purpose & allowedPurposes) != 0;
+ boolean hasDisallowedPurpose = (purpose & ~allowedPurposes) != 0;
+ return hasAllowedPurpose && !hasDisallowedPurpose;
+ }
+
private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec)
throws InvalidAlgorithmParameterException {
if (spec.getAttestKeyAlias() != null) {
@@ -473,6 +522,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mRSAPublicExponent = null;
mRng = null;
mKeyStore = null;
+ mEcCurveName = null;
}
private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
@@ -514,13 +564,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
case KeymasterDefs.KM_ALGORITHM_EC:
if (algSpecificSpec instanceof ECGenParameterSpec) {
ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
- String curveName = ecSpec.getName();
- Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
- curveName.toLowerCase(Locale.US));
+ mEcCurveName = ecSpec.getName();
+ final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get(
+ mEcCurveName.toLowerCase(Locale.US));
if (ecSpecKeySizeBits == null) {
throw new InvalidAlgorithmParameterException(
- "Unsupported EC curve name: " + curveName
- + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
+ "Unsupported EC curve name: " + mEcCurveName
+ + ". Supported: " + SUPPORTED_EC_CURVE_NAMES);
}
if (mKeySizeBits == -1) {
mKeySizeBits = ecSpecKeySizeBits;
@@ -744,7 +794,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
params.add(KeyStore2ParameterUtils.makeEnum(
- Tag.EC_CURVE, keySize2EcCurve(mKeySizeBits)
+ Tag.EC_CURVE, keySizeAndNameToEcCurve(mKeySizeBits, mEcCurveName)
));
}
@@ -864,7 +914,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private static void checkValidKeySize(
int keymasterAlgorithm,
int keySize,
- boolean isStrongBoxBacked)
+ boolean isStrongBoxBacked,
+ String mEcCurveName)
throws InvalidAlgorithmParameterException {
switch (keymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_EC:
@@ -873,9 +924,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
"Unsupported StrongBox EC key size: "
+ keySize + " bits. Supported: 256");
}
- if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) {
+ if (isStrongBoxBacked && isCurve25519(mEcCurveName)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported StrongBox EC: " + mEcCurveName);
+ }
+ if (!SUPPORTED_EC_CURVE_SIZES.contains(keySize)) {
throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
- + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
+ + keySize + " bits. Supported: " + SUPPORTED_EC_CURVE_SIZES);
}
break;
case KeymasterDefs.KM_ALGORITHM_RSA:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 72a145fa6a05..358104fffbf6 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -66,6 +66,11 @@ public class AndroidKeyStoreProvider extends Provider {
private static final String DESEDE_SYSTEM_PROPERTY =
"ro.hardware.keystore_desede";
+ // Conscrypt returns the Ed25519 OID as the JCA key algorithm.
+ private static final String ED25519_OID = "1.3.101.112";
+ // Conscrypt returns "XDH" as the X25519 JCA key algorithm.
+ private static final String X25519_ALIAS = "XDH";
+
/** @hide **/
public AndroidKeyStoreProvider() {
super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
@@ -78,10 +83,16 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
+ put("KeyPairGenerator." + X25519_ALIAS,
+ PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
+ put("KeyPairGenerator." + ED25519_OID,
+ PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// java.security.KeyFactory
putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA");
+ putKeyFactoryImpl(X25519_ALIAS);
+ putKeyFactoryImpl(ED25519_OID);
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
@@ -219,12 +230,17 @@ public class AndroidKeyStoreProvider extends Provider {
KeyStoreSecurityLevel securityLevel = iSecurityLevel;
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
-
return new AndroidKeyStoreECPublicKey(descriptor, metadata,
iSecurityLevel, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) {
return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
iSecurityLevel, (RSAPublicKey) publicKey);
+ } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) {
+ //TODO(b/214203951) missing classes in conscrypt
+ throw new ProviderException("Curve " + ED25519_OID + " not supported yet");
+ } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) {
+ //TODO(b/214203951) missing classes in conscrypt
+ throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet");
} else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ jcaKeyAlgorithm);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 9b414681c0fa..8d5fdfbc8cb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -52,6 +52,9 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
*/
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+ public static final boolean IS_ENABLED = SystemProperties
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
"persist.debug.back_predictability_progress_threshold";
private static final int PROGRESS_THRESHOLD = SystemProperties
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 c6a68dc98da6..58f79f3600de 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
@@ -1608,7 +1608,9 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "addBubble: " + bubble);
}
- if (getBubbleCount() == 0 && shouldShowStackEdu()) {
+ final boolean firstBubble = getBubbleCount() == 0;
+
+ if (firstBubble && shouldShowStackEdu()) {
// Override the default stack position if we're showing user education.
mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
}
@@ -1621,7 +1623,7 @@ public class BubbleStackView extends FrameLayout
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
- if (getBubbleCount() == 0) {
+ if (firstBubble) {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
// Set the dot position to the opposite of the side the stack is resting on, since the stack
@@ -1652,6 +1654,10 @@ public class BubbleStackView extends FrameLayout
bubble.cleanupViews();
}
updateExpandedView();
+ if (getBubbleCount() == 0 && !isExpanded()) {
+ // This is the last bubble and the stack is collapsed
+ updateStackPosition();
+ }
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
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 8a482fbfa1c4..b52c8d118711 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
@@ -502,16 +502,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) {
wct.setBounds(task1.token, mBounds1);
+ wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1));
mWinBounds1.set(mBounds1);
mWinToken1 = task1.token;
}
if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) {
wct.setBounds(task2.token, mBounds2);
+ wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2));
mWinBounds2.set(mBounds2);
mWinToken2 = task2.token;
}
}
+ private int getSmallestWidthDp(Rect bounds) {
+ final int minWidth = Math.min(bounds.width(), bounds.height());
+ final float density = mContext.getResources().getDisplayMetrics().density;
+ return (int) (minWidth / density);
+ }
+
/**
* Shift configuration bounds to prevent client apps get configuration changed or relaunch. And
* restore shifted configuration bounds if it's no longer shifted.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c94f3d197dea..0362b3fba36b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -699,7 +699,10 @@ public abstract class WMShellBaseModule {
Context context,
@ShellMainThread ShellExecutor shellExecutor
) {
- return Optional.of(
- new BackAnimationController(shellExecutor, context));
+ if (BackAnimationController.IS_ENABLED) {
+ return Optional.of(
+ new BackAnimationController(shellExecutor, context));
+ }
+ return Optional.empty();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 91615fe05417..f01457bc4c95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -84,6 +84,7 @@ public class PipTransition extends PipTransitionController {
private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
+ private SurfaceControl.Transaction mFinishTransaction;
private final Rect mExitDestinationBounds = new Rect();
@Nullable
private IBinder mExitTransition;
@@ -166,6 +167,7 @@ public class PipTransition extends PipTransitionController {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null, null);
mFinishCallback = null;
+ mFinishTransaction = null;
throw new RuntimeException("Previous callback not called, aborting exit PIP.");
}
@@ -267,7 +269,8 @@ public class PipTransition extends PipTransitionController {
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
@Nullable SurfaceControl.Transaction tx) {
- if (isInPipDirection(direction)) {
+ final boolean enteringPip = isInPipDirection(direction);
+ if (enteringPip) {
mPipTransitionState.setTransitionState(ENTERED_PIP);
}
// If there is an expected exit transition, then the exit will be "merged" into this
@@ -279,6 +282,20 @@ public class PipTransition extends PipTransitionController {
if (tx != null) {
wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
+ final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
+ final int displayRotation = taskInfo.getConfiguration().windowConfiguration
+ .getDisplayRotation();
+ if (enteringPip && mInFixedRotation && mEndFixedRotation != displayRotation
+ && leash != null && leash.isValid()) {
+ // Launcher may update the Shelf height during the animation, which will update the
+ // destination bounds. Because this is in fixed rotation, We need to make sure the
+ // finishTransaction is using the updated bounds in the display rotation.
+ final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+ final Rect finishBounds = new Rect(destinationBounds);
+ rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
+ mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
+ }
+ mFinishTransaction = null;
mFinishCallback.onTransitionFinished(wct, null /* callback */);
mFinishCallback = null;
}
@@ -290,6 +307,7 @@ public class PipTransition extends PipTransitionController {
if (mFinishCallback == null) return;
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
+ mFinishTransaction = null;
}
@Override
@@ -336,6 +354,7 @@ public class PipTransition extends PipTransitionController {
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(wct, wctCB);
};
+ mFinishTransaction = finishTransaction;
// Check if it is Shell rotation.
if (Transitions.SHELL_TRANSITIONS_ROTATION) {
@@ -526,6 +545,7 @@ public class PipTransition extends PipTransitionController {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
+ mFinishTransaction = null;
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
}
@@ -549,6 +569,7 @@ public class PipTransition extends PipTransitionController {
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
mFinishCallback = finishCallback;
+ mFinishTransaction = finishTransaction;
final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
startTransaction, finishTransaction, enterPip.getStartRotation(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 082fe9205be8..22dd9b953be7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -49,14 +48,13 @@ class MainStage extends StageTaskListener {
return mIsActive;
}
- void activate(Rect rootBounds, WindowContainerTransaction wct, boolean includingTopTask) {
+ void activate(WindowContainerTransaction wct, boolean includingTopTask) {
if (mIsActive) return;
final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds)
- // Moving the root task to top after the child tasks were re-parented , or the root
- // task cannot be visible and focused.
- .reorder(rootToken, true /* onTop */);
+ // Moving the root task to top after the child tasks were re-parented , or the root
+ // task cannot be visible and focused.
+ wct.reorder(rootToken, true /* onTop */);
if (includingTopTask) {
wct.reparentTasks(
null /* currentParent */,
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 76641f0e6c21..7318f4860b1c 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
@@ -354,8 +354,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
- mSideStage.setBounds(getSideStageBounds(), wct);
+ mMainStage.activate(wct, false /* reparent */);
+ updateWindowBounds(mSplitLayout, wct);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -470,13 +470,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.setDivideRatio(splitRatio);
if (mMainStage.isActive()) {
- mMainStage.moveToTop(getMainStageBounds(), wct);
+ mMainStage.moveToTop(wct);
} else {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
+ mMainStage.activate(wct, false /* reparent */);
}
- mSideStage.moveToTop(getSideStageBounds(), wct);
+ mSideStage.moveToTop(wct);
+ updateWindowBounds(mSplitLayout, wct);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -774,8 +775,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
- mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */);
- mSideStage.moveToTop(getSideStageBounds(), wct);
+ mMainStage.activate(wct, true /* includingTopTask */);
+ mSideStage.moveToTop(wct);
+ updateWindowBounds(mSplitLayout, wct);
}
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -997,10 +999,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Exit to side stage if main stage no longer has children.
exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED);
}
- } else if (isSideStage) {
+ } else if (isSideStage && !mMainStage.isActive()) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
- // Make sure the main stage is active.
prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 83534c178469..c5aab45f56fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -53,8 +53,8 @@ import java.io.PrintWriter;
* Base class that handle common task org. related for split-screen stages.
* Note that this class and its sub-class do not directly perform hierarchy operations.
* They only serve to hold a collection of tasks and provide APIs like
- * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
- * to perform operations in-sync with other containers.
+ * {@link #addTask(ActivityManager.RunningTaskInfo, WindowContainerTransaction)} for the centralized
+ * {@link StageCoordinator} to perform hierarchy operations in-sync with other containers.
*
* @see StageCoordinator
*/
@@ -310,13 +310,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
}
- void moveToTop(Rect rootBounds, WindowContainerTransaction wct) {
+ void moveToTop(WindowContainerTransaction wct) {
final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
- }
-
- void setBounds(Rect bounds, WindowContainerTransaction wct) {
- wct.setBounds(mRootTaskInfo.token, bounds);
+ wct.reorder(rootToken, true /* onTop */);
}
void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 025bcad07955..33aa018923a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -69,6 +69,7 @@ import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -115,8 +116,10 @@ public class SplashscreenContentDrawer {
private final Handler mSplashscreenWorkerHandler;
@VisibleForTesting
final ColorCache mColorCache;
+ private final ShellExecutor mSplashScreenExecutor;
- SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool) {
+ SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool,
+ ShellExecutor splashScreenExecutor) {
mContext = context;
mIconProvider = iconProvider;
mTransactionPool = pool;
@@ -129,6 +132,7 @@ public class SplashscreenContentDrawer {
shellSplashscreenWorkerThread.start();
mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler);
+ mSplashScreenExecutor = splashScreenExecutor;
}
/**
@@ -397,7 +401,7 @@ public class SplashscreenContentDrawer {
SplashScreenView build() {
Drawable iconDrawable;
- final int animationDuration;
+ final long animationDuration;
if (mSuggestType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
|| mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
// empty or legacy splash screen case
@@ -455,8 +459,8 @@ public class SplashscreenContentDrawer {
iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
} else {
mFinalIconDrawables = SplashscreenIconDrawableFactory.makeIconDrawable(
- mTmpAttrs.mIconBgColor, mThemeColor,
- iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
+ mTmpAttrs.mIconBgColor, mThemeColor, iconDrawable, mDefaultIconSize,
+ mFinalIconSize, mSplashscreenWorkerHandler, mSplashScreenExecutor);
}
}
@@ -516,7 +520,7 @@ public class SplashscreenContentDrawer {
}
private SplashScreenView fillViewWithIcon(int iconSize, @Nullable Drawable[] iconDrawable,
- int animationDuration, Consumer<Runnable> uiThreadInitTask) {
+ long animationDuration, Consumer<Runnable> uiThreadInitTask) {
Drawable foreground = null;
Drawable background = null;
if (iconDrawable != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 54281e0199e3..fdd5a1578f41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -18,9 +18,7 @@ package com.android.wm.shell.startingsurface;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +34,8 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Animatable;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Trace;
@@ -44,6 +44,9 @@ import android.util.PathParser;
import android.window.SplashScreenView;
import com.android.internal.R;
+import com.android.wm.shell.common.ShellExecutor;
+
+import java.util.function.LongConsumer;
/**
* Creating a lightweight Drawable object used for splash screen.
@@ -60,14 +63,15 @@ public class SplashscreenIconDrawableFactory {
*/
static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
@NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
- Handler splashscreenWorkerHandler) {
+ Handler splashscreenWorkerHandler, ShellExecutor splashScreenExecutor) {
Drawable foreground;
Drawable background = null;
boolean drawBackground =
backgroundColor != Color.TRANSPARENT && backgroundColor != themeColor;
if (foregroundDrawable instanceof Animatable) {
- foreground = new AnimatableIconAnimateListener(foregroundDrawable);
+ foreground = new AnimatableIconAnimateListener(foregroundDrawable,
+ splashScreenExecutor);
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
// If the icon is Adaptive, we already use the icon background.
drawBackground = false;
@@ -266,99 +270,107 @@ public class SplashscreenIconDrawableFactory {
*/
public static class AnimatableIconAnimateListener extends AdaptiveForegroundDrawable
implements SplashScreenView.IconAnimateListener {
- private Animatable mAnimatableIcon;
- private Animator mIconAnimator;
+ private final Animatable mAnimatableIcon;
private boolean mAnimationTriggered;
private AnimatorListenerAdapter mJankMonitoringListener;
+ private boolean mRunning;
+ private long mDuration;
+ private LongConsumer mStartListener;
+ private final ShellExecutor mSplashScreenExecutor;
- AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable) {
+ AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable,
+ ShellExecutor splashScreenExecutor) {
super(foregroundDrawable);
- mForegroundDrawable.setCallback(mCallback);
- }
-
- @Override
- public void setAnimationJankMonitoring(AnimatorListenerAdapter listener) {
- mJankMonitoringListener = listener;
- }
-
- @Override
- public boolean prepareAnimate(long duration, Runnable startListener) {
- mAnimatableIcon = (Animatable) mForegroundDrawable;
- mIconAnimator = ValueAnimator.ofInt(0, 1);
- mIconAnimator.setDuration(duration);
- mIconAnimator.addListener(new Animator.AnimatorListener() {
+ Callback callback = new Callback() {
@Override
- public void onAnimationStart(Animator animation) {
- if (startListener != null) {
- startListener.run();
- }
- try {
- if (mJankMonitoringListener != null) {
- mJankMonitoringListener.onAnimationStart(animation);
- }
- mAnimatableIcon.start();
- } catch (Exception ex) {
- Log.e(TAG, "Error while running the splash screen animated icon", ex);
- animation.cancel();
- }
+ public void invalidateDrawable(@NonNull Drawable who) {
+ invalidateSelf();
}
@Override
- public void onAnimationEnd(Animator animation) {
- mAnimatableIcon.stop();
- if (mJankMonitoringListener != null) {
- mJankMonitoringListener.onAnimationEnd(animation);
- }
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what,
+ long when) {
+ scheduleSelf(what, when);
}
@Override
- public void onAnimationCancel(Animator animation) {
- mAnimatableIcon.stop();
- if (mJankMonitoringListener != null) {
- mJankMonitoringListener.onAnimationCancel(animation);
- }
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ unscheduleSelf(what);
}
+ };
+ mForegroundDrawable.setCallback(callback);
+ mSplashScreenExecutor = splashScreenExecutor;
+ mAnimatableIcon = (Animatable) mForegroundDrawable;
+ }
- @Override
- public void onAnimationRepeat(Animator animation) {
- // do not repeat
- mAnimatableIcon.stop();
- }
- });
- return true;
+ @Override
+ public void setAnimationJankMonitoring(AnimatorListenerAdapter listener) {
+ mJankMonitoringListener = listener;
}
@Override
- public void stopAnimation() {
- if (mIconAnimator != null && mIconAnimator.isRunning()) {
- mIconAnimator.end();
- mJankMonitoringListener = null;
- }
+ public void prepareAnimate(long duration, LongConsumer startListener) {
+ stopAnimation();
+ mDuration = duration;
+ mStartListener = startListener;
}
- private final Callback mCallback = new Callback() {
- @Override
- public void invalidateDrawable(@NonNull Drawable who) {
- invalidateSelf();
+ private void startAnimation() {
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationStart(null);
+ }
+ try {
+ mAnimatableIcon.start();
+ } catch (Exception ex) {
+ Log.e(TAG, "Error while running the splash screen animated icon", ex);
+ mRunning = false;
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationCancel(null);
+ }
+ if (mStartListener != null) {
+ mStartListener.accept(mDuration);
+ }
+ return;
}
+ long animDuration = mDuration;
+ if (mAnimatableIcon instanceof AnimatedVectorDrawable
+ && ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration() > 0) {
+ animDuration = ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration();
+ } else if (mAnimatableIcon instanceof AnimationDrawable
+ && ((AnimationDrawable) mAnimatableIcon).getTotalDuration() > 0) {
+ animDuration = ((AnimationDrawable) mAnimatableIcon).getTotalDuration();
+ }
+ mRunning = true;
+ mSplashScreenExecutor.executeDelayed(this::stopAnimation, animDuration);
+ if (mStartListener != null) {
+ mStartListener.accept(Math.max(animDuration, 0));
+ }
+ }
- @Override
- public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
- scheduleSelf(what, when);
+ private void onAnimationEnd() {
+ mAnimatableIcon.stop();
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationEnd(null);
}
+ mStartListener = null;
+ mRunning = false;
+ }
- @Override
- public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
- unscheduleSelf(what);
+ @Override
+ public void stopAnimation() {
+ if (mRunning) {
+ mSplashScreenExecutor.removeCallbacks(this::stopAnimation);
+ onAnimationEnd();
+ mJankMonitoringListener = null;
}
- };
+ }
private void ensureAnimationStarted() {
if (mAnimationTriggered) {
return;
}
- if (mIconAnimator != null && !mIconAnimator.isRunning()) {
- mIconAnimator.start();
+ if (!mRunning) {
+ startAnimation();
}
mAnimationTriggered = true;
}
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 34426991a459..04d6ef7f9505 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
@@ -148,7 +148,8 @@ public class StartingSurfaceDrawer {
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mSplashScreenExecutor = splashScreenExecutor;
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool,
+ mSplashScreenExecutor);
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
mWindowManagerGlobal = WindowManagerGlobal.getInstance();
mDisplayManager.getDisplay(DEFAULT_DISPLAY);
@@ -364,6 +365,12 @@ public class StartingSurfaceDrawer {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
final SplashScreenView contentView = viewSupplier.get();
record.mBGColor = contentView.getInitBackgroundColor();
+ } else {
+ // release the icon view host
+ final SplashScreenView contentView = viewSupplier.get();
+ if (contentView.getSurfaceHost() != null) {
+ SplashScreenView.releaseIconHost(contentView.getSurfaceHost());
+ }
}
} catch (RuntimeException e) {
// don't crash if something else bad happens, for example a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4bc5850cd293..56d51687603a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -348,12 +348,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final boolean isTask = change.getTaskInfo() != null;
+ boolean isSeamlessDisplayChange = false;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
if (info.getType() == TRANSIT_CHANGE) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
mTransactionPool, startTransaction, change, info.getRootLeash(),
anim);
@@ -387,6 +388,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - info.getRootOffset().x,
change.getEndAbsBounds().top - info.getRootOffset().y);
+ // Seamless display transition doesn't need to animate.
+ if (isSeamlessDisplayChange) continue;
if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
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 83d5f04b7cdb..daec336fc71e 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
@@ -28,8 +28,10 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.window.WindowContainerTransaction;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +40,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayImeController;
import org.junit.Before;
@@ -56,6 +59,7 @@ public class SplitLayoutTests extends ShellTestCase {
@Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
+ @Mock WindowContainerTransaction mWct;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
private SplitLayout mSplitLayout;
@@ -149,6 +153,16 @@ public class SplitLayoutTests extends ShellTestCase {
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
}
+ @Test
+ public void testApplyTaskChanges_updatesSmallestScreenWidthDp() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+ mSplitLayout.applyTaskChanges(mWct, task1, task2);
+
+ verify(mWct).setSmallestScreenWidthDp(eq(task1.token), anyInt());
+ verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt());
+ }
+
private void waitDividerFlingFinished() {
verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture());
mRunnableCaptor.getValue().run();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index c9720671f49c..0639ad5d0a62 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -67,8 +67,7 @@ public class MainStageTests extends ShellTestCase {
@Test
public void testActiveDeactivate() {
- mMainStage.activate(mRootTaskInfo.configuration.windowConfiguration.getBounds(), mWct,
- true /* reparent */);
+ mMainStage.activate(mWct, true /* reparent */);
assertThat(mMainStage.isActive()).isTrue();
mMainStage.deactivate(mWct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 59c377a3e13d..19d2a7ef6f45 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -44,7 +44,6 @@ import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
import android.app.ActivityManager;
-import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -421,8 +420,7 @@ public class SplitTransitionTests extends ShellTestCase {
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
- mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction(),
- true /* includingTopTask */);
+ mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */);
}
private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index f140c68b19db..f3e15084d730 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -144,8 +144,8 @@ public final class SatellitePvt implements Parcelable {
private final ClockInfo mClockInfo;
private final double mIonoDelayMeters;
private final double mTropoDelayMeters;
- private final long mTimeOfClock;
- private final long mTimeOfEphemeris;
+ private final long mTimeOfClockSeconds;
+ private final long mTimeOfEphemerisSeconds;
private final int mIssueOfDataClock;
private final int mIssueOfDataEphemeris;
@EphemerisSource
@@ -457,8 +457,8 @@ public final class SatellitePvt implements Parcelable {
@Nullable ClockInfo clockInfo,
double ionoDelayMeters,
double tropoDelayMeters,
- long timeOfClock,
- long timeOfEphemeris,
+ long timeOfClockSeconds,
+ long timeOfEphemerisSeconds,
int issueOfDataClock,
int issueOfDataEphemeris,
@EphemerisSource int ephemerisSource) {
@@ -468,8 +468,8 @@ public final class SatellitePvt implements Parcelable {
mClockInfo = clockInfo;
mIonoDelayMeters = ionoDelayMeters;
mTropoDelayMeters = tropoDelayMeters;
- mTimeOfClock = timeOfClock;
- mTimeOfEphemeris = timeOfEphemeris;
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ mTimeOfEphemerisSeconds = timeOfEphemerisSeconds;
mIssueOfDataClock = issueOfDataClock;
mIssueOfDataEphemeris = issueOfDataEphemeris;
mEphemerisSource = ephemerisSource;
@@ -545,31 +545,31 @@ public final class SatellitePvt implements Parcelable {
}
/**
- * Time of Clock.
+ * Time of Clock in seconds.
*
* <p>The value is in seconds since GPS epoch, regardless of the constellation.
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * <p>This field is valid if {@link #hasTimeOfClock()} is true.
+ * <p>This field is valid if {@link #hasTimeOfClockSeconds()} is true.
*/
@IntRange(from = 0)
- public long getTimeOfClock() {
- return mTimeOfClock;
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
}
/**
- * Time of ephemeris.
+ * Time of ephemeris in seconds.
*
* <p>The value is in seconds since GPS epoch, regardless of the constellation.
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * <p>This field is valid if {@link #hasTimeOfEphemeris()} is true.
+ * <p>This field is valid if {@link #hasTimeOfEphemerisSeconds()} is true.
*/
@IntRange(from = 0)
- public long getTimeOfEphemeris() {
- return mTimeOfEphemeris;
+ public long getTimeOfEphemerisSeconds() {
+ return mTimeOfEphemerisSeconds;
}
/**
@@ -607,13 +607,13 @@ public final class SatellitePvt implements Parcelable {
return (mFlags & HAS_ISSUE_OF_DATA_EPHEMERIS) != 0;
}
- /** Returns {@code true} if {@link #getTimeOfClock()} ()} is valid. */
- public boolean hasTimeOfClock() {
+ /** Returns {@code true} if {@link #getTimeOfClockSeconds()} ()} is valid. */
+ public boolean hasTimeOfClockSeconds() {
return (mFlags & HAS_TIME_OF_CLOCK) != 0;
}
- /** Returns {@code true} if {@link #getTimeOfEphemeris()} is valid. */
- public boolean hasTimeOfEphemeris() {
+ /** Returns {@code true} if {@link #getTimeOfEphemerisSeconds()} is valid. */
+ public boolean hasTimeOfEphemerisSeconds() {
return (mFlags & HAS_TIME_OF_EPHEMERIS) != 0;
}
@@ -671,8 +671,8 @@ public final class SatellitePvt implements Parcelable {
parcel.writeParcelable(mClockInfo, flags);
parcel.writeDouble(mIonoDelayMeters);
parcel.writeDouble(mTropoDelayMeters);
- parcel.writeLong(mTimeOfClock);
- parcel.writeLong(mTimeOfEphemeris);
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeLong(mTimeOfEphemerisSeconds);
parcel.writeInt(mIssueOfDataClock);
parcel.writeInt(mIssueOfDataEphemeris);
parcel.writeInt(mEphemerisSource);
@@ -687,8 +687,8 @@ public final class SatellitePvt implements Parcelable {
+ ", ClockInfo=" + mClockInfo
+ ", IonoDelayMeters=" + mIonoDelayMeters
+ ", TropoDelayMeters=" + mTropoDelayMeters
- + ", TimeOfClock=" + mTimeOfClock
- + ", TimeOfEphemeris=" + mTimeOfEphemeris
+ + ", TimeOfClockSeconds=" + mTimeOfClockSeconds
+ + ", TimeOfEphemerisSeconds=" + mTimeOfEphemerisSeconds
+ ", IssueOfDataClock=" + mIssueOfDataClock
+ ", IssueOfDataEphemeris=" + mIssueOfDataEphemeris
+ ", EphemerisSource=" + mEphemerisSource
@@ -709,8 +709,8 @@ public final class SatellitePvt implements Parcelable {
@Nullable private ClockInfo mClockInfo;
private double mIonoDelayMeters;
private double mTropoDelayMeters;
- private long mTimeOfClock;
- private long mTimeOfEphemeris;
+ private long mTimeOfClockSeconds;
+ private long mTimeOfEphemerisSeconds;
private int mIssueOfDataClock;
private int mIssueOfDataEphemeris;
@EphemerisSource
@@ -796,13 +796,13 @@ public final class SatellitePvt implements Parcelable {
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * @param timeOfClock time of clock (seconds)
+ * @param timeOfClockSeconds time of clock (seconds)
* @return builder object
*/
@NonNull
- public Builder setTimeOfClock(@IntRange(from = 0) long timeOfClock) {
- Preconditions.checkArgumentNonnegative(timeOfClock);
- mTimeOfClock = timeOfClock;
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ Preconditions.checkArgumentNonnegative(timeOfClockSeconds);
+ mTimeOfClockSeconds = timeOfClockSeconds;
mFlags = (byte) (mFlags | HAS_TIME_OF_CLOCK);
return this;
}
@@ -814,13 +814,13 @@ public final class SatellitePvt implements Parcelable {
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * @param timeOfEphemeris time of ephemeris (seconds)
+ * @param timeOfEphemerisSeconds time of ephemeris (seconds)
* @return builder object
*/
@NonNull
- public Builder setTimeOfEphemeris(@IntRange(from = 0) int timeOfEphemeris) {
- Preconditions.checkArgumentNonnegative(timeOfEphemeris);
- mTimeOfEphemeris = timeOfEphemeris;
+ public Builder setTimeOfEphemerisSeconds(@IntRange(from = 0) long timeOfEphemerisSeconds) {
+ Preconditions.checkArgumentNonnegative(timeOfEphemerisSeconds);
+ mTimeOfEphemerisSeconds = timeOfEphemerisSeconds;
mFlags = (byte) (mFlags | HAS_TIME_OF_EPHEMERIS);
return this;
}
@@ -879,7 +879,8 @@ public final class SatellitePvt implements Parcelable {
@NonNull
public SatellitePvt build() {
return new SatellitePvt(mFlags, mPositionEcef, mVelocityEcef, mClockInfo,
- mIonoDelayMeters, mTropoDelayMeters, mTimeOfClock, mTimeOfEphemeris,
+ mIonoDelayMeters, mTropoDelayMeters, mTimeOfClockSeconds,
+ mTimeOfEphemerisSeconds,
mIssueOfDataClock, mIssueOfDataEphemeris,
mEphemerisSource);
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6e695e68fbf2..3a2f0d9194f5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -91,7 +91,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* AudioManager provides access to volume and ringer mode control.
@@ -5625,27 +5624,33 @@ public class AudioManager {
* Note that the information may be imprecise when the implementation
* cannot distinguish whether a particular device is enabled.
*
- * {@hide}
+ * @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
+ * will have multi-bit device types.
+ * Prefer to use {@link #getDevicesForAttributes()} instead,
+ * noting that getDevicesForStream() has a few small discrepancies
+ * for better volume handling.
+ * @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public int getDevicesForStream(int streamType) {
switch (streamType) {
- case STREAM_VOICE_CALL:
- case STREAM_SYSTEM:
- case STREAM_RING:
- case STREAM_MUSIC:
- case STREAM_ALARM:
- case STREAM_NOTIFICATION:
- case STREAM_DTMF:
- case STREAM_ACCESSIBILITY:
- final IAudioService service = getService();
- try {
- return service.getDevicesForStream(streamType);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- default:
- return 0;
+ case STREAM_VOICE_CALL:
+ case STREAM_SYSTEM:
+ case STREAM_RING:
+ case STREAM_MUSIC:
+ case STREAM_ALARM:
+ case STREAM_NOTIFICATION:
+ case STREAM_DTMF:
+ case STREAM_ACCESSIBILITY:
+ final IAudioService service = getService();
+ try {
+ return service.getDeviceMaskForStream(streamType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ default:
+ return 0;
}
}
@@ -8349,100 +8354,97 @@ public class AudioManager {
}
/**
- * Add UID's that can be considered as assistant.
+ * Add UIDs that can be considered as assistant.
*
- * @param assistantUids UID's of the services that can be considered as assistant.
+ * @param assistantUids UIDs of the services that can be considered as assistant.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void addAssistantServicesUids(@NonNull List<Integer> assistantUids) {
+ public void addAssistantServicesUids(@NonNull int[] assistantUids) {
try {
- getService().addAssistantServicesUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().addAssistantServicesUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Remove UID's that can be considered as assistant.
+ * Remove UIDs that can be considered as assistant.
*
- * @param assistantUids UID'S of the services that should be remove.
+ * @param assistantUids UIDs of the services that should be remove.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void removeAssistantServicesUids(@NonNull List<Integer> assistantUids) {
+ public void removeAssistantServicesUids(@NonNull int[] assistantUids) {
try {
- getService().removeAssistantServicesUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().removeAssistantServicesUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Get the list of assistants UIDs that been added with the
- * {@link #addAssistantServicesUids(List)} (List)} and not yet removed with
- * {@link #removeAssistantServicesUids(List)}
+ * Get the assistants UIDs that been added with the
+ * {@link #addAssistantServicesUids(int[])} and not yet removed with
+ * {@link #removeAssistantServicesUids(int[])}
*
- * @return list of assistants UID's
+ * @return array of assistants UIDs
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @NonNull List<Integer> getAssistantServicesUids() {
+ public @NonNull int[] getAssistantServicesUids() {
try {
int[] uids = getService().getAssistantServicesUids();
- return Arrays.stream(uids).boxed().collect(Collectors.toList());
+ return Arrays.copyOf(uids, uids.length);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Sets UID's that can be considered as active assistant. Calling the API with a new list will
- * overwrite previous list. If the list of UIDs is empty then no UID will be considered active.
- * In this manner calling the API with an empty list will remove all UID's previously set.
+ * Sets UIDs that can be considered as active assistant. Calling the API with a new array will
+ * overwrite previous UIDs. If the array of UIDs is empty then no UID will be considered active.
+ * In this manner calling the API with an empty array will remove all UIDs previously set.
*
- * @param assistantUids UID'S of the services that can be considered active assistant. Can be
- * an empty list, for this no UID will be considered active.
+ * @param assistantUids UIDs of the services that can be considered active assistant. Can be
+ * an empty array, for this no UID will be considered active.
*
* <p> Note that during audio service crash reset and after boot up the list of active assistant
- * UID's will be reset to an empty list (i.e. no UID will be considered as an active assistant).
+ * UIDs will be reset to an empty list (i.e. no UID will be considered as an active assistant).
* Just after user switch the list of active assistant will also reset to empty.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setActiveAssistantServiceUids(@NonNull List<Integer> assistantUids) {
+ public void setActiveAssistantServiceUids(@NonNull int[] assistantUids) {
try {
- getService().setActiveAssistantServiceUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().setActiveAssistantServiceUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Get the list of active assistant UIDs last set with the
- * {@link #setActiveAssistantServiceUids(List)}
+ * Get active assistant UIDs last set with the
+ * {@link #setActiveAssistantServiceUids(int[])}
*
- * @return list of active assistants UID's
+ * @return array of active assistants UIDs
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @NonNull List<Integer> getActiveAssistantServicesUids() {
+ public @NonNull int[] getActiveAssistantServicesUids() {
try {
int[] uids = getService().getActiveAssistantServiceUids();
- return Arrays.stream(uids).boxed().collect(Collectors.toList());
+ return Arrays.copyOf(uids, uids.length);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 6cacebb433a6..acb34b527e06 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
import android.media.audio.common.AidlConversion;
import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioProductStrategy;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
@@ -47,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeSet;
/* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
* TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -1655,9 +1657,63 @@ public class AudioSystem
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static native boolean getMasterMute();
- /** @hide */
+ /** @hide
+ * Only used (unsupported) for legacy apps.
+ * @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
+ * will have multi-bit device types.
+ * Use {@link AudioManager#getDevicesForAttributes(AudioAttributes)} instead.
+ */
@UnsupportedAppUsage
- public static native int getDevicesForStream(int stream);
+ @Deprecated
+ public static int getDevicesForStream(int stream) {
+ final AudioAttributes attr =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
+ return getDeviceMaskFromSet(generateAudioDeviceTypesSet(
+ getDevicesForAttributes(attr, true /* forVolume */)));
+ }
+
+ /** @hide
+ * Conversion from a device set to a bit mask.
+ *
+ * Legacy compatibility method (use a device list instead of a bit mask).
+ * Conversion to bit mask skips multi-bit (S and later) internal device types
+ * (e.g. AudioSystem.DEVICE_OUT* or AudioManager.DEVICE_OUT*) for legacy
+ * compatibility reasons. Legacy apps will not understand these new device types
+ * and it will raise false matches with old device types.
+ */
+ public static int getDeviceMaskFromSet(@NonNull Set<Integer> deviceSet) {
+ int deviceMask = DEVICE_NONE; // zero.
+ int deviceInChecksum = DEVICE_BIT_IN;
+ for (Integer device : deviceSet) {
+ if ((device & (device - 1) & ~DEVICE_BIT_IN) != 0) {
+ Log.v(TAG, "getDeviceMaskFromSet skipping multi-bit device value " + device);
+ continue;
+ }
+ deviceMask |= device;
+ deviceInChecksum &= device;
+ }
+ // Validate that deviceSet is either ALL input devices or ALL output devices.
+ // We check that the "OR" of all the DEVICE_BIT_INs == the "AND" of all DEVICE_BIT_INs.
+ if (!deviceSet.isEmpty() && deviceInChecksum != (deviceMask & DEVICE_BIT_IN)) {
+ Log.e(TAG, "getDeviceMaskFromSet: Invalid set: " + deviceSetToString(deviceSet));
+ }
+ return deviceMask;
+ }
+
+ /** @hide */
+ @NonNull
+ public static String deviceSetToString(@NonNull Set<Integer> devices) {
+ int n = 0;
+ StringBuilder sb = new StringBuilder();
+ for (Integer device : devices) {
+ if (n++ > 0) {
+ sb.append(", ");
+ }
+ sb.append(AudioSystem.getDeviceName(device));
+ sb.append("(" + Integer.toHexString(device) + ")");
+ }
+ return sb.toString();
+ }
/**
* @hide
@@ -1668,13 +1724,14 @@ public class AudioSystem
* otherwise (typically one device, except for duplicated paths).
*/
public static @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
Objects.requireNonNull(attributes);
final AudioDeviceAttributes[] devices = new AudioDeviceAttributes[MAX_DEVICE_ROUTING];
- final int res = getDevicesForAttributes(attributes, devices);
+ final int res = getDevicesForAttributes(attributes, devices, forVolume);
final ArrayList<AudioDeviceAttributes> routeDevices = new ArrayList<>();
if (res != SUCCESS) {
- Log.e(TAG, "error " + res + " in getDevicesForAttributes for " + attributes);
+ Log.e(TAG, "error " + res + " in getDevicesForAttributes attributes: " + attributes
+ + " forVolume: " + forVolume);
return routeDevices;
}
@@ -1693,7 +1750,8 @@ public class AudioSystem
private static final int MAX_DEVICE_ROUTING = 4;
private static native int getDevicesForAttributes(@NonNull AudioAttributes aa,
- @NonNull AudioDeviceAttributes[] devices);
+ @NonNull AudioDeviceAttributes[] devices,
+ boolean forVolume);
/** @hide returns true if master mono is enabled. */
public static native boolean getMasterMono();
@@ -2250,18 +2308,15 @@ public class AudioSystem
/**
* @hide
- * Return a set of audio device types from a bit mask audio device type, which may
+ * Return a set of audio device types from a list of audio device attributes, which may
* represent multiple audio device types.
- * FIXME: Remove this when getting ride of bit mask usage of audio device types.
- */
- public static Set<Integer> generateAudioDeviceTypesSet(int types) {
- Set<Integer> deviceTypes = new HashSet<>();
- Set<Integer> allDeviceTypes =
- (types & DEVICE_BIT_IN) == 0 ? DEVICE_OUT_ALL_SET : DEVICE_IN_ALL_SET;
- for (int deviceType : allDeviceTypes) {
- if ((types & deviceType) == deviceType) {
- deviceTypes.add(deviceType);
- }
+ */
+ @NonNull
+ public static Set<Integer> generateAudioDeviceTypesSet(
+ @NonNull List<AudioDeviceAttributes> deviceList) {
+ Set<Integer> deviceTypes = new TreeSet<>();
+ for (AudioDeviceAttributes device : deviceList) {
+ deviceTypes.add(device.getInternalType());
}
return deviceTypes;
}
@@ -2272,7 +2327,7 @@ public class AudioSystem
*/
public static Set<Integer> intersectionAudioDeviceTypes(
@NonNull Set<Integer> a, @NonNull Set<Integer> b) {
- Set<Integer> intersection = new HashSet<>(a);
+ Set<Integer> intersection = new TreeSet<>(a);
intersection.retainAll(b);
return intersection;
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d702eb9aef57..fa3057a4ead1 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -360,7 +360,7 @@ interface IAudioService {
boolean isMusicActive(in boolean remotely);
- int getDevicesForStream(in int streamType);
+ int getDeviceMaskForStream(in int streamType);
int[] getAvailableCommunicationDeviceIds();
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 50bb79ccaa0b..9ed8770a455c 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -15,6 +15,7 @@
*/
//#define LOG_NDEBUG 0
+#include <utility>
#define LOG_TAG "SoundPool::Stream"
#include <utils/Log.h>
#include <android/content/AttributionSourceState.h>
@@ -309,13 +310,11 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
}
if (mAudioTrack == nullptr) {
// mToggle toggles each time a track is started on a given stream.
- // The toggle is concatenated with the Stream address and passed to AudioTrack
- // as callback user data. This enables the detection of callbacks received from the old
+ // This enables the detection of callbacks received from the old
// audio track while the new one is being started and avoids processing them with
// wrong audio audio buffer size (mAudioBufferSize)
auto toggle = mToggle ^ 1;
// NOLINTNEXTLINE(performance-no-int-to-ptr)
- void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
audio_channel_mask_t soundChannelMask = sound->getChannelMask();
// When sound contains a valid channel mask, use it as is.
// Otherwise, use stream count to calculate channel mask.
@@ -327,10 +326,11 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
android::content::AttributionSourceState attributionSource;
attributionSource.packageName = mStreamManager->getOpPackageName();
attributionSource.token = sp<BBinder>::make();
+ mCallback = sp<StreamCallback>::make(this, toggle),
// TODO b/182469354 make consistent with AudioRecord, add util for native source
mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
- staticCallback, userData,
+ mCallback,
0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_DEFAULT,
nullptr /*offloadInfo*/, attributionSource,
@@ -375,16 +375,55 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
-/* static */
-void Stream::staticCallback(int event, void* user, void* info)
-{
- const auto userAsInt = (uintptr_t)user;
- // NOLINTNEXTLINE(performance-no-int-to-ptr)
- auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
- stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
+int Stream::getCorrespondingStreamID() {
+ std::lock_guard lock(mLock);
+ return static_cast<int>(mAudioTrack ? mStreamID : getPairStream()->mStreamID);
+}
+size_t Stream::StreamCallback::onMoreData(const AudioTrack::Buffer&) {
+ ALOGW("%s streamID %d Unexpected EVENT_MORE_DATA for static track",
+ __func__, mStream->getCorrespondingStreamID());
+ return 0;
+}
+
+void Stream::StreamCallback::onUnderrun() {
+ ALOGW("%s streamID %d Unexpected EVENT_UNDERRUN for static track",
+ __func__, mStream->getCorrespondingStreamID());
+}
+
+void Stream::StreamCallback::onLoopEnd(int32_t) {
+ ALOGV("%s streamID %d EVENT_LOOP_END", __func__, mStream->getCorrespondingStreamID());
+}
+
+void Stream::StreamCallback::onMarker(uint32_t) {
+ ALOGW("%s streamID %d Unexpected EVENT_MARKER for static track",
+ __func__, mStream->getCorrespondingStreamID());
+}
+
+void Stream::StreamCallback::onNewPos(uint32_t) {
+ ALOGW("%s streamID %d Unexpected EVENT_NEW_POS for static track",
+ __func__, mStream->getCorrespondingStreamID());
}
-void Stream::callback(int event, void* info, int toggle, int tries)
+void Stream::StreamCallback::onBufferEnd() {
+ mStream->onBufferEnd(mToggle, 0);
+}
+
+void Stream::StreamCallback::onNewIAudioTrack() {
+ ALOGV("%s streamID %d NEW_IAUDIOTRACK", __func__, mStream->getCorrespondingStreamID());
+}
+
+void Stream::StreamCallback::onStreamEnd() {
+ ALOGW("%s streamID %d Unexpected EVENT_STREAM_END for static track",
+ __func__, mStream->getCorrespondingStreamID());
+}
+
+size_t Stream::StreamCallback::onCanWriteMoreData(const AudioTrack::Buffer&) {
+ ALOGW("%s streamID %d Unexpected EVENT_CAN_WRITE_MORE_DATA for static track",
+ __func__, mStream->getCorrespondingStreamID());
+ return 0;
+}
+
+void Stream::onBufferEnd(int toggle, int tries)
{
int32_t activeStreamIDToRestart = 0;
{
@@ -400,7 +439,7 @@ void Stream::callback(int event, void* info, int toggle, int tries)
if (tries < 3) {
lock.unlock();
ALOGV("%s streamID %d going to pair stream", __func__, (int)mStreamID);
- getPairStream()->callback(event, info, toggle, tries + 1);
+ getPairStream()->onBufferEnd(toggle, tries + 1);
} else {
ALOGW("%s streamID %d cannot find track", __func__, (int)mStreamID);
}
@@ -410,31 +449,10 @@ void Stream::callback(int event, void* info, int toggle, int tries)
ALOGD("%s streamID %d wrong toggle", __func__, (int)mStreamID);
return;
}
- switch (event) {
- case AudioTrack::EVENT_MORE_DATA:
- ALOGW("%s streamID %d Invalid EVENT_MORE_DATA for static track",
- __func__, (int)mStreamID);
- break;
- case AudioTrack::EVENT_UNDERRUN:
- ALOGW("%s streamID %d Invalid EVENT_UNDERRUN for static track",
- __func__, (int)mStreamID);
- break;
- case AudioTrack::EVENT_BUFFER_END:
- ALOGV("%s streamID %d EVENT_BUFFER_END", __func__, (int)mStreamID);
- if (mState != IDLE) {
- activeStreamIDToRestart = mStreamID;
- mStopTimeNs = systemTime();
- }
- break;
- case AudioTrack::EVENT_LOOP_END:
- ALOGV("%s streamID %d EVENT_LOOP_END", __func__, (int)mStreamID);
- break;
- case AudioTrack::EVENT_NEW_IAUDIOTRACK:
- ALOGV("%s streamID %d NEW_IAUDIOTRACK", __func__, (int)mStreamID);
- break;
- default:
- ALOGW("%s streamID %d Invalid event %d", __func__, (int)mStreamID, event);
- break;
+ ALOGV("%s streamID %d EVENT_BUFFER_END", __func__, (int)mStreamID);
+ if (mState != IDLE) {
+ activeStreamIDToRestart = mStreamID;
+ mStopTimeNs = systemTime();
}
} // lock ends here. This is on the callback thread, no need to be precise.
if (activeStreamIDToRestart > 0) {
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
index aa0eef5bc66e..0054eeca529a 100644
--- a/media/jni/soundpool/Stream.h
+++ b/media/jni/soundpool/Stream.h
@@ -124,6 +124,35 @@ public:
// This never changes. See top of header.
Stream* getPairStream() const;
+ // Stream ID of ourselves, or the pair depending on who holds the AudioTrack
+ int getCorrespondingStreamID();
+
+protected:
+ // AudioTrack callback interface implementation
+ class StreamCallback : public AudioTrack::IAudioTrackCallback {
+ public:
+ StreamCallback(Stream * stream, bool toggle) : mStream(stream), mToggle(toggle) {}
+ size_t onMoreData(const AudioTrack::Buffer& buffer) override;
+ void onUnderrun() override;
+ void onLoopEnd(int32_t loopsRemaining) override;
+ void onMarker(uint32_t markerPosition) override;
+ void onNewPos(uint32_t newPos) override;
+ void onBufferEnd() override;
+ void onNewIAudioTrack() override;
+ void onStreamEnd() override;
+ size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override;
+
+ // Holding a raw ptr is technically unsafe, but, Stream objects persist
+ // through the lifetime of the StreamManager through the use of a
+ // unique_ptr<Stream[]>. Ensuring lifetime will cause us to give up
+ // locality as well as pay RefBase/sp performance cost, which we are
+ // unwilling to do. Non-owning refs to unique_ptrs are idiomatically raw
+ // ptrs, as below.
+ Stream * const mStream;
+ const bool mToggle;
+ };
+
+ sp<StreamCallback> mCallback;
private:
// garbage is used to release tracks and data outside of any lock.
void play_l(const std::shared_ptr<Sound>& sound, int streamID,
@@ -133,9 +162,7 @@ private:
void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
// For use with AudioTrack callback.
- static void staticCallback(int event, void* user, void* info);
- void callback(int event, void* info, int toggle, int tries)
- NO_THREAD_SAFETY_ANALYSIS; // uses unique_lock
+ void onBufferEnd(int toggle, int tries) NO_THREAD_SAFETY_ANALYSIS;
// StreamManager should be set on construction and not changed.
// release mLock before calling into StreamManager
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
deleted file mode 100644
index f23794b50543..000000000000
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
+++ /dev/null
@@ -1,72 +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.mediaframeworktest.unit;
-
-import static org.junit.Assert.assertEquals;
-
-import android.bluetooth.BluetoothProfile;
-import android.media.BluetoothProfileConnectionInfo;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class BluetoothProfileConnectionInfoTest {
-
- @Test
- public void testCoverageA2dp() {
- final boolean supprNoisy = false;
- final int volume = 42;
- final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
- .createA2dpInfo(supprNoisy, volume);
- assertEquals(info.getProfile(), BluetoothProfile.A2DP);
- assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
- assertEquals(info.getVolume(), volume);
- }
-
- @Test
- public void testCoverageA2dpSink() {
- final int volume = 42;
- final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
- .createA2dpSinkInfo(volume);
- assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
- assertEquals(info.getVolume(), volume);
- }
-
- @Test
- public void testCoveragehearingAid() {
- final boolean supprNoisy = true;
- final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
- .createHearingAidInfo(supprNoisy);
- assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
- assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
- }
-
- @Test
- public void testCoverageLeAudio() {
- final boolean supprNoisy = false;
- final boolean isLeOutput = true;
- final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
- .createLeAudioInfo(supprNoisy, isLeOutput);
- assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
- assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
- assertEquals(info.isLeOutput(), isLeOutput);
- }
-}
-
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 0c360519ceb2..65428de95519 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -48,6 +48,7 @@ private:
static APerformanceHintManager* create(sp<IHintManager> iHintManager);
sp<IHintManager> mHintManager;
+ const sp<IBinder> mToken = sp<BBinder>::make();
const int64_t mPreferredRateNanos;
};
@@ -119,11 +120,10 @@ APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manage
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
- sp<IBinder> token = sp<BBinder>::make();
std::vector<int32_t> tids(threadIds, threadIds + size);
sp<IHintSession> session;
binder::Status ret =
- mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session);
+ mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
if (!ret.isOk() || !session) {
return nullptr;
}
diff --git a/obex/Android.bp b/obex/Android.bp
index 37e7f76bead5..d89d41de2895 100644
--- a/obex/Android.bp
+++ b/obex/Android.bp
@@ -44,6 +44,8 @@ license {
],
}
+// No longer used. Only kept because the ObexPacket class is a public API.
+// The library has been migrated to platform/external/obex.
java_sdk_library {
name: "javax.obex",
srcs: ["javax/**/*.java"],
diff --git a/obex/javax/obex/ApplicationParameter.java b/obex/javax/obex/ApplicationParameter.java
deleted file mode 100644
index 16770a1aef31..000000000000
--- a/obex/javax/obex/ApplicationParameter.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * @hide
- */
-public final class ApplicationParameter {
-
- private byte[] mArray;
-
- private int mLength;
-
- private int mMaxLength = 1000;
-
- public static class TRIPLET_TAGID {
- public static final byte ORDER_TAGID = 0x01;
-
- public static final byte SEARCH_VALUE_TAGID = 0x02;
-
- public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
-
- // if equals to "0", PSE only reply number of contacts
- public static final byte MAXLISTCOUNT_TAGID = 0x04;
-
- public static final byte LISTSTARTOFFSET_TAGID = 0x05;
-
- public static final byte PROPERTY_SELECTOR_TAGID = 0x06;
-
- public static final byte FORMAT_TAGID = 0x07;
-
- // only used if max list count = 0
- public static final byte PHONEBOOKSIZE_TAGID = 0x08;
-
- // only used in "mch" in response
- public static final byte NEWMISSEDCALLS_TAGID = 0x09;
-
- public static final byte SUPPORTEDFEATURE_TAGID = 0x10;
-
- public static final byte PRIMARYVERSIONCOUNTER_TAGID = 0x0A;
-
- public static final byte SECONDARYVERSIONCOUNTER_TAGID = 0x0B;
-
- public static final byte VCARDSELECTOR_TAGID = 0x0C;
-
- public static final byte DATABASEIDENTIFIER_TAGID = 0x0D;
-
- public static final byte VCARDSELECTOROPERATOR_TAGID = 0x0E;
-
- public static final byte RESET_NEW_MISSED_CALLS_TAGID = 0x0F;
- }
-
- public static class TRIPLET_VALUE {
- public static class ORDER {
- public static final byte ORDER_BY_INDEX = 0x00;
-
- public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
-
- public static final byte ORDER_BY_PHONETIC = 0x02;
- }
-
- public static class SEARCHATTRIBUTE {
- public static final byte SEARCH_BY_NAME = 0x00;
-
- public static final byte SEARCH_BY_NUMBER = 0x01;
-
- public static final byte SEARCH_BY_SOUND = 0x02;
- }
-
- public static class FORMAT {
- public static final byte VCARD_VERSION_21 = 0x00;
-
- public static final byte VCARD_VERSION_30 = 0x01;
- }
- }
-
- public static class TRIPLET_LENGTH {
- public static final byte ORDER_LENGTH = 1;
-
- public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
-
- public static final byte MAXLISTCOUNT_LENGTH = 2;
-
- public static final byte LISTSTARTOFFSET_LENGTH = 2;
-
- public static final byte PROPERTY_SELECTOR_LENGTH = 8;
-
- public static final byte FORMAT_LENGTH = 1;
-
- public static final byte PHONEBOOKSIZE_LENGTH = 2;
-
- public static final byte NEWMISSEDCALLS_LENGTH = 1;
-
- public static final byte SUPPORTEDFEATURE_LENGTH = 4;
-
- public static final byte PRIMARYVERSIONCOUNTER_LENGTH = 16;
-
- public static final byte SECONDARYVERSIONCOUNTER_LENGTH = 16;
-
- public static final byte VCARDSELECTOR_LENGTH = 8;
-
- public static final byte DATABASEIDENTIFIER_LENGTH = 16;
-
- public static final byte VCARDSELECTOROPERATOR_LENGTH = 1;
-
- public static final byte RESETNEWMISSEDCALLS_LENGTH = 1;
- }
-
- public ApplicationParameter() {
- mArray = new byte[mMaxLength];
- mLength = 0;
- }
-
- public void addAPPHeader(byte tag, byte len, byte[] value) {
- if ((mLength + len + 2) > mMaxLength) {
- byte[] array_tmp = new byte[mLength + 4 * len];
- System.arraycopy(mArray, 0, array_tmp, 0, mLength);
- mArray = array_tmp;
- mMaxLength = mLength + 4 * len;
- }
- mArray[mLength++] = tag;
- mArray[mLength++] = len;
- System.arraycopy(value, 0, mArray, mLength, len);
- mLength += len;
- }
-
- public byte[] getAPPparam() {
- byte[] para = new byte[mLength];
- System.arraycopy(mArray, 0, para, 0, mLength);
- return para;
- }
-}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
deleted file mode 100644
index ec226fb7ada3..000000000000
--- a/obex/javax/obex/Authenticator.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * This interface provides a way to respond to authentication challenge and
- * authentication response headers. When a client or server receives an
- * authentication challenge or authentication response header, the
- * <code>onAuthenticationChallenge()</code> or
- * <code>onAuthenticationResponse()</code> will be called, respectively, by the
- * implementation.
- * <P>
- * For more information on how the authentication procedure works in OBEX,
- * please review the IrOBEX specification at <A
- * HREF="http://www.irda.org">http://www.irda.org</A>.
- * <P>
- * <STRONG>Authentication Challenges</STRONG>
- * <P>
- * When a client or server receives an authentication challenge header, the
- * <code>onAuthenticationChallenge()</code> method will be invoked by the OBEX
- * API implementation. The application will then return the user name (if
- * needed) and password via a <code>PasswordAuthentication</code> object. The
- * password in this object is not sent in the authentication response. Instead,
- * the 16-byte challenge received in the authentication challenge is combined
- * with the password returned from the <code>onAuthenticationChallenge()</code>
- * method and passed through the MD5 hash algorithm. The resulting value is sent
- * in the authentication response along with the user name if it was provided.
- * <P>
- * <STRONG>Authentication Responses</STRONG>
- * <P>
- * When a client or server receives an authentication response header, the
- * <code>onAuthenticationResponse()</code> method is invoked by the API
- * implementation with the user name received in the authentication response
- * header. (The user name will be <code>null</code> if no user name was provided
- * in the authentication response header.) The application must determine the
- * correct password. This value should be returned from the
- * <code>onAuthenticationResponse()</code> method. If the authentication request
- * should fail without the implementation checking the password,
- * <code>null</code> should be returned by the application. (This is needed for
- * reasons like not recognizing the user name, etc.) If the returned value is
- * not <code>null</code>, the OBEX API implementation will combine the password
- * returned from the <code>onAuthenticationResponse()</code> method and
- * challenge sent via the authentication challenge, apply the MD5 hash
- * algorithm, and compare the result to the response hash received in the
- * authentication response header. If the values are not equal, an
- * <code>IOException</code> will be thrown if the client requested
- * authentication. If the server requested authentication, the
- * <code>onAuthenticationFailure()</code> method will be called on the
- * <code>ServerRequestHandler</code> that failed authentication. The connection
- * is <B>not</B> closed if authentication failed.
- * @hide
- */
-public interface Authenticator {
-
- /**
- * Called when a client or a server receives an authentication challenge
- * header. It should respond to the challenge with a
- * <code>PasswordAuthentication</code> that contains the correct user name
- * and password for the challenge.
- * @param description the description of which user name and password should
- * be used; if no description is provided in the authentication
- * challenge or the description is encoded in an encoding scheme that
- * is not supported, an empty string will be provided
- * @param isUserIdRequired <code>true</code> if the user ID is required;
- * <code>false</code> if the user ID is not required
- * @param isFullAccess <code>true</code> if full access to the server will
- * be granted; <code>false</code> if read only access will be granted
- * @return a <code>PasswordAuthentication</code> object containing the user
- * name and password used for authentication
- */
- PasswordAuthentication onAuthenticationChallenge(String description, boolean isUserIdRequired,
- boolean isFullAccess);
-
- /**
- * Called when a client or server receives an authentication response
- * header. This method will provide the user name and expect the correct
- * password to be returned.
- * @param userName the user name provided in the authentication response; may
- * be <code>null</code>
- * @return the correct password for the user name provided; if
- * <code>null</code> is returned then the authentication request
- * failed
- */
- byte[] onAuthenticationResponse(byte[] userName);
-}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
deleted file mode 100644
index 022ad4fb4a34..000000000000
--- a/obex/javax/obex/BaseStream.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-/**
- * This interface defines the methods needed by a parent that uses the
- * PrivateInputStream and PrivateOutputStream objects defined in this package.
- * @hide
- */
-public interface BaseStream {
-
- /**
- * Verifies that this object is still open.
- * @throws IOException if the object is closed
- */
- void ensureOpen() throws IOException;
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * @throws IOException if the operation is completed
- */
- void ensureNotDone() throws IOException;
-
- /**
- * Continues the operation since there is no data to read.
- * @param sendEmpty <code>true</code> if the operation should send an empty
- * packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or is
- * output stream
- * @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
- * @throws IOException if an IO error occurs
- */
- boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
-
- /**
- * Called when the output or input stream is closed.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- void streamClosed(boolean inStream) throws IOException;
-}
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
deleted file mode 100644
index c627dfb8abac..000000000000
--- a/obex/javax/obex/ClientOperation.java
+++ /dev/null
@@ -1,851 +0,0 @@
-/*
- * Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-
-import android.util.Log;
-
-/**
- * This class implements the <code>Operation</code> interface. It will read and
- * write data via puts and gets.
- * @hide
- */
-public final class ClientOperation implements Operation, BaseStream {
-
- private static final String TAG = "ClientOperation";
-
- private static final boolean V = ObexHelper.VDBG;
-
- private ClientSession mParent;
-
- private boolean mInputOpen;
-
- private PrivateInputStream mPrivateInput;
-
- private boolean mPrivateInputOpen;
-
- private PrivateOutputStream mPrivateOutput;
-
- private boolean mPrivateOutputOpen;
-
- private String mExceptionMessage;
-
- private int mMaxPacketSize;
-
- private boolean mOperationDone;
-
- private boolean mGetOperation;
-
- private boolean mGetFinalFlag;
-
- private HeaderSet mRequestHeader;
-
- private HeaderSet mReplyHeader;
-
- private boolean mEndOfBodySent;
-
- private boolean mSendBodyHeader = true;
- // A latch - when triggered, there is not way back ;-)
- private boolean mSrmActive = false;
-
- // Assume SRM disabled - until support is confirmed
- // by the server
- private boolean mSrmEnabled = false;
- // keep waiting until final-bit is received in request
- // to handle the case where the SRM enable header is in
- // a different OBEX packet than the SRMP header.
- private boolean mSrmWaitingForRemote = true;
-
-
- /**
- * Creates new OperationImpl to read and write data to a server
- * @param maxSize the maximum packet size
- * @param p the parent to this object
- * @param type <code>true</code> if this is a get request;
- * <code>false</code. if this is a put request
- * @param header the header to set in the initial request
- * @throws IOException if the an IO error occurred
- */
- public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
- throws IOException {
-
- mParent = p;
- mEndOfBodySent = false;
- mInputOpen = true;
- mOperationDone = false;
- mMaxPacketSize = maxSize;
- mGetOperation = type;
- mGetFinalFlag = false;
-
- mPrivateInputOpen = false;
- mPrivateOutputOpen = false;
- mPrivateInput = null;
- mPrivateOutput = null;
-
- mReplyHeader = new HeaderSet();
-
- mRequestHeader = new HeaderSet();
-
- int[] headerList = header.getHeaderList();
-
- if (headerList != null) {
-
- for (int i = 0; i < headerList.length; i++) {
- mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
- }
- }
-
- if ((header).mAuthChall != null) {
- mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
- System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
- (header).mAuthChall.length);
- }
-
- if ((header).mAuthResp != null) {
- mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
- System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
- (header).mAuthResp.length);
-
- }
-
- if ((header).mConnectionID != null) {
- mRequestHeader.mConnectionID = new byte[4];
- System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
- 4);
-
- }
- }
-
- /**
- * Allows to set flag which will force GET to be always sent as single packet request with
- * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
- * require requests to be sent this way.
- */
- public void setGetFinalFlag(boolean flag) {
- mGetFinalFlag = flag;
- }
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server called this method
- */
- public synchronized void abort() throws IOException {
- ensureOpen();
- //no compatible with sun-ri
- if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
- throw new IOException("Operation has already ended");
- }
-
- mExceptionMessage = "Operation aborted";
- if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- mOperationDone = true;
- /*
- * Since we are not sending any headers or returning any headers then
- * we just need to write and read the same bytes
- */
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
- throw new IOException("Invalid response code from server");
- }
-
- mExceptionMessage = null;
- }
-
- close();
- }
-
- /**
- * Retrieves the response code retrieved from the server. Response codes are
- * defined in the <code>ResponseCodes</code> interface.
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet</code> in a <code>ClientSession</code>
- * object
- */
- public synchronized int getResponseCode() throws IOException {
- if ((mReplyHeader.responseCode == -1)
- || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- validateConnection();
- }
-
- return mReplyHeader.responseCode;
- }
-
- /**
- * This method will always return <code>null</code>
- * @return <code>null</code>
- */
- public String getEncoding() {
- return null;
- }
-
- /**
- * Returns the type of content that the resource connected to is providing.
- * E.g. if the connection is via HTTP, then the value of the content-type
- * header field is returned.
- * @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
- */
- public String getType() {
- try {
- return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
- } catch (IOException e) {
- if(V) Log.d(TAG, "Exception occured - returning null",e);
- return null;
- }
- }
-
- /**
- * Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header field
- * is returned.
- * @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
- */
- public long getLength() {
- try {
- Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
-
- if (temp == null) {
- return -1;
- } else {
- return temp.longValue();
- }
- } catch (IOException e) {
- if(V) Log.d(TAG,"Exception occured - returning -1",e);
- return -1;
- }
- }
-
- /**
- * Open and return an input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public InputStream openInputStream() throws IOException {
-
- ensureOpen();
-
- if (mPrivateInputOpen)
- throw new IOException("no more input streams available");
- if (mGetOperation) {
- // send the GET request here
- validateConnection();
- } else {
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- }
-
- mPrivateInputOpen = true;
-
- return mPrivateInput;
- }
-
- /**
- * Open and return a data input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public DataInputStream openDataInputStream() throws IOException {
- return new DataInputStream(openInputStream());
- }
-
- /**
- * Open and return an output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public OutputStream openOutputStream() throws IOException {
-
- ensureOpen();
- ensureNotDone();
-
- if (mPrivateOutputOpen)
- throw new IOException("no more output streams available");
-
- if (mPrivateOutput == null) {
- // there are 3 bytes operation headers and 3 bytes body headers //
- mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
- }
-
- mPrivateOutputOpen = true;
-
- return mPrivateOutput;
- }
-
- public int getMaxPacketSize() {
- return mMaxPacketSize - 6 - getHeaderLength();
- }
-
- public int getHeaderLength() {
- // OPP may need it
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- return headerArray.length;
- }
-
- /**
- * Open and return a data output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public DataOutputStream openDataOutputStream() throws IOException {
- return new DataOutputStream(openOutputStream());
- }
-
- /**
- * Closes the connection and ends the transaction
- * @throws IOException if the operation has already ended or is closed
- */
- public void close() throws IOException {
- mInputOpen = false;
- mPrivateInputOpen = false;
- mPrivateOutputOpen = false;
- mParent.setRequestInactive();
- }
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- public HeaderSet getReceivedHeader() throws IOException {
- ensureOpen();
-
- return mReplyHeader;
- }
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- * @throws NullPointerException if <code>headers</code> is <code>null</code>
- */
- public void sendHeaders(HeaderSet headers) throws IOException {
- ensureOpen();
- if (mOperationDone) {
- throw new IOException("Operation has already exchanged all data");
- }
-
- if (headers == null) {
- throw new IOException("Headers may not be null");
- }
-
- int[] headerList = headers.getHeaderList();
- if (headerList != null) {
- for (int i = 0; i < headerList.length; i++) {
- mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
- }
- }
- }
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * @throws IOException if the operation is completed
- */
- public void ensureNotDone() throws IOException {
- if (mOperationDone) {
- throw new IOException("Operation has completed");
- }
- }
-
- /**
- * Verifies that the connection is open and no exceptions should be thrown.
- * @throws IOException if an exception needs to be thrown
- */
- public void ensureOpen() throws IOException {
- mParent.ensureOpen();
-
- if (mExceptionMessage != null) {
- throw new IOException(mExceptionMessage);
- }
- if (!mInputOpen) {
- throw new IOException("Operation has already ended");
- }
- }
-
- /**
- * Verifies that the connection is open and the proper data has been read.
- * @throws IOException if an IO error occurs
- */
- private void validateConnection() throws IOException {
- ensureOpen();
-
- // Make sure that a response has been recieved from remote
- // before continuing
- if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
- startProcessing();
- }
- }
-
- /**
- * Sends a request to the client of the specified type.
- * This function will enable SRM and set SRM active if the server
- * response allows this.
- * @param opCode the request code to send to the client
- * @return <code>true</code> if there is more data to send;
- * <code>false</code> if there is no more data to send
- * @throws IOException if an IO error occurs
- */
- private boolean sendRequest(int opCode) throws IOException {
- boolean returnValue = false;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int bodyLength = -1;
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
- if (mPrivateOutput != null) {
- bodyLength = mPrivateOutput.size();
- }
-
- /*
- * Determine if there is space to add a body request. At present
- * this method checks to see if there is room for at least a 17
- * byte body header. This number needs to be at least 6 so that
- * there is room for the header ID and length and the reply ID and
- * length, but it is a waste of resources if we can't send much of
- * the body.
- */
- final int MINIMUM_BODY_LENGTH = 3;
- if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
- > mMaxPacketSize) {
- int end = 0;
- int start = 0;
- // split & send the headerArray in multiple packets.
-
- while (end != headerArray.length) {
- //split the headerArray
-
- end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
- - ObexHelper.BASE_PACKET_LENGTH);
- // can not split
- if (end == -1) {
- mOperationDone = true;
- abort();
- mExceptionMessage = "Header larger then can be sent in a packet";
- mInputOpen = false;
-
- if (mPrivateInput != null) {
- mPrivateInput.close();
- }
-
- if (mPrivateOutput != null) {
- mPrivateOutput.close();
- }
- throw new IOException("OBEX Packet exceeds max packet size");
- }
-
- byte[] sendHeader = new byte[end - start];
- System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
- if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
- return false;
- }
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- return false;
- }
-
- start = end;
- }
-
- // Enable SRM if it should be enabled
- checkForSrm();
-
- if (bodyLength > 0) {
- return true;
- } else {
- return false;
- }
- } else {
- /* All headers will fit into a single package */
- if(mSendBodyHeader == false) {
- /* As we are not to send any body data, set the FINAL_BIT */
- opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
- }
- out.write(headerArray);
- }
-
- if (bodyLength > 0) {
- /*
- * Determine if we can send the whole body or just part of
- * the body. Remember that there is the 3 bytes for the
- * response message and 3 bytes for the header ID and length
- */
- if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
- returnValue = true;
-
- bodyLength = mMaxPacketSize - headerArray.length - 6;
- }
-
- byte[] body = mPrivateOutput.readBytes(bodyLength);
-
- /*
- * Since this is a put request if the final bit is set or
- * the output stream is closed we need to send the 0x49
- * (End of Body) otherwise, we need to send 0x48 (Body)
- */
- if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
- && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
- out.write(HeaderSet.END_OF_BODY);
- mEndOfBodySent = true;
- } else {
- out.write(HeaderSet.BODY);
- }
-
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
-
- if (body != null) {
- out.write(body);
- }
- }
-
- if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
- // only 0x82 or 0x83 can send 0x49
- if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
- out.write(HeaderSet.BODY);
- } else {
- out.write(HeaderSet.END_OF_BODY);
- mEndOfBodySent = true;
- }
-
- bodyLength = 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- }
-
- if (out.size() == 0) {
- if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
- return false;
- }
- // Enable SRM if it should be enabled
- checkForSrm();
- return returnValue;
- }
- if ((out.size() > 0)
- && (!mParent.sendRequest(opCode, out.toByteArray(),
- mReplyHeader, mPrivateInput, mSrmActive))) {
- return false;
- }
- // Enable SRM if it should be enabled
- checkForSrm();
-
- // send all of the output data in 0x48,
- // send 0x49 with empty body
- if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
- returnValue = true;
-
- return returnValue;
- }
-
- private void checkForSrm() throws IOException {
- Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if(mParent.isSrmSupported() == true && srmMode != null
- && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
- mSrmEnabled = true;
- }
- /**
- * Call this only when a complete obex packet have been received.
- * (This is not optimal, but the current design is not really suited to
- * the way SRM is specified.)
- * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
- * into every OBEX packet, hence if another header occupies the entire packet,
- * the scheme will not work - unlikely though.
- */
- if(mSrmEnabled) {
- mSrmWaitingForRemote = false;
- Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
- mSrmWaitingForRemote = true;
- // Clear the wait header, as the absence of the header in the next packet
- // indicates don't wait anymore.
- mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- }
- if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
- mSrmActive = true;
- }
- }
-
- /**
- * This method starts the processing thread results. It will send the
- * initial request. If the response takes more then one packet, a thread
- * will be started to handle additional requests
- * @throws IOException if an IO error occurs
- */
- private synchronized void startProcessing() throws IOException {
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- boolean more = true;
-
- if (mGetOperation) {
- if (!mOperationDone) {
- if (!mGetFinalFlag) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode ==
- ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
- }
- // For GET we need to loop until all headers have been sent,
- // And then we wait for the first continue package with the
- // reply.
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- }
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- } else {
- checkForSrm();
- }
- } else {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
-
- if (more) {
- throw new IOException("FINAL_GET forced, data didn't fit into one packet");
- }
-
- mOperationDone = true;
- }
- }
- } else {
- // PUT operation
- if (!mOperationDone) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- }
- }
-
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- }
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- }
- }
-
- /**
- * Continues the operation since there is no data to read.
- * @param sendEmpty <code>true</code> if the operation should send an empty
- * packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or is
- * output stream
- * @throws IOException if an IO error occurs
- */
- public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
- throws IOException {
-
- // One path to the first put operation - the other one does not need to
- // handle SRM, as all will fit into one packet.
-
- if (mGetOperation) {
- if ((inStream) && (!mOperationDone)) {
- // to deal with inputstream in get operation
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- /*
- * Determine if that was not the last packet in the operation
- */
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- } else {
- checkForSrm();
- }
-
- return true;
-
- } else if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in get operation
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
-
- if (!mGetFinalFlag) {
- sendRequest(ObexHelper.OBEX_OPCODE_GET);
- } else {
- sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
- }
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- return true;
-
- } else if (mOperationDone) {
- return false;
- }
-
- } else {
- // PUT operation
- if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in put operation
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
- sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- return true;
- } else if ((inStream) && (!mOperationDone)) {
- // How to deal with inputstream in put operation ?
- return false;
-
- } else if (mOperationDone) {
- return false;
- }
-
- }
- return false;
- }
-
- /**
- * Called when the output or input stream is closed.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- public void streamClosed(boolean inStream) throws IOException {
- if (!mGetOperation) {
- if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in put operation
-
- boolean more = true;
-
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- if (headerArray.length <= 0)
- more = false;
- }
- // If have not sent any data so send all now
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
-
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- }
-
- /*
- * According to the IrOBEX specification, after the final put, you
- * only have a single reply to send. so we don't need the while
- * loop.
- */
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-
- sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
- }
- mOperationDone = true;
- } else if ((inStream) && (mOperationDone)) {
- // how to deal with input stream in put stream ?
- mOperationDone = true;
- }
- } else {
- if ((inStream) && (!mOperationDone)) {
-
- // to deal with inputstream in get operation
- // Have not sent any data so send it all now
-
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
-
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
- if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
- break;
- }
- }
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
- mReplyHeader, mPrivateInput, false);
- // Regardless of the SRM state, wait for the response.
- }
- mOperationDone = true;
- } else if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in get operation
- // part of the data may have been sent in continueOperation.
-
- boolean more = true;
-
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- if (headerArray.length <= 0)
- more = false;
- }
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
- more = false;
-
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
- }
- sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
- // parent.sendRequest(0x83, null, replyHeaders, privateInput);
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- }
- }
- }
-
- public void noBodyHeader(){
- mSendBodyHeader = false;
- }
-}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
deleted file mode 100644
index 272a920754f5..000000000000
--- a/obex/javax/obex/ClientSession.java
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import android.util.Log;
-
-/**
- * This class in an implementation of the OBEX ClientSession.
- * @hide
- */
-public final class ClientSession extends ObexSession {
-
- private static final String TAG = "ClientSession";
-
- private boolean mOpen;
-
- // Determines if an OBEX layer connection has been established
- private boolean mObexConnected;
-
- private byte[] mConnectionId = null;
-
- /*
- * The max Packet size must be at least 255 according to the OBEX
- * specification.
- */
- private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
-
- private boolean mRequestActive;
-
- private final InputStream mInput;
-
- private final OutputStream mOutput;
-
- private final boolean mLocalSrmSupported;
-
- private final ObexTransport mTransport;
-
- public ClientSession(final ObexTransport trans) throws IOException {
- mInput = trans.openInputStream();
- mOutput = trans.openOutputStream();
- mOpen = true;
- mRequestActive = false;
- mLocalSrmSupported = trans.isSrmSupported();
- mTransport = trans;
- }
-
- /**
- * Create a ClientSession
- * @param trans The transport to use for OBEX transactions
- * @param supportsSrm True if Single Response Mode should be used e.g. if the
- * supplied transport is a TCP or l2cap channel.
- * @throws IOException if it occurs while opening the transport streams.
- */
- public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
- mInput = trans.openInputStream();
- mOutput = trans.openOutputStream();
- mOpen = true;
- mRequestActive = false;
- mLocalSrmSupported = supportsSrm;
- mTransport = trans;
- }
-
- public HeaderSet connect(final HeaderSet header) throws IOException {
- ensureOpen();
- if (mObexConnected) {
- throw new IOException("Already connected to server");
- }
- setRequestActive();
-
- int totalLength = 4;
- byte[] head = null;
-
- // Determine the header byte array
- if (header != null) {
- if (header.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
- }
- head = ObexHelper.createHeader(header, false);
- totalLength += head.length;
- }
- /*
- * Write the OBEX CONNECT packet to the server.
- * Byte 0: 0x80
- * Byte 1&2: Connect Packet Length
- * Byte 3: OBEX Version Number (Presently, 0x10)
- * Byte 4: Flags (For TCP 0x00)
- * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
- * Byte 7 to n: headers
- */
- byte[] requestPacket = new byte[totalLength];
- int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
- // We just need to start at byte 3 since the sendRequest() method will
- // handle the length and 0x80.
- requestPacket[0] = (byte)0x10;
- requestPacket[1] = (byte)0x00;
- requestPacket[2] = (byte)(maxRxPacketSize >> 8);
- requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
- if (head != null) {
- System.arraycopy(head, 0, requestPacket, 4, head.length);
- }
-
- // Since we are not yet connected, the peer max packet size is unknown,
- // hence we are only guaranteed the server will use the first 7 bytes.
- if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
- throw new IOException("Packet size exceeds max packet size for connect");
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
-
- /*
- * Read the response from the OBEX server.
- * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
- * Byte 1&2: Packet Length
- * Byte 3: OBEX Version Number
- * Byte 4: Flags3
- * Byte 5&6: Max OBEX packet Length
- * Byte 7 to n: Optional HeaderSet
- */
- if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
- mObexConnected = true;
- }
- setRequestInactive();
-
- return returnHeaderSet;
- }
-
- public Operation get(HeaderSet header) throws IOException {
-
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
-
- HeaderSet head;
- if (header == null) {
- head = new HeaderSet();
- } else {
- head = header;
- if (head.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- head.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
- }
-
- if(mLocalSrmSupported) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
- /* TODO: Consider creating an interface to get the wait state.
- * On an android system, I cannot see when this is to be used.
- * except perhaps if we are to wait for user accept on a push message.
- if(getLocalWaitState()) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
- }
- */
- }
-
- return new ClientOperation(mMaxTxPacketSize, this, head, true);
- }
-
- /**
- * 0xCB Connection Id an identifier used for OBEX connection multiplexing
- */
- public void setConnectionID(long id) {
- if ((id < 0) || (id > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Connection ID is not in a valid range");
- }
- mConnectionId = ObexHelper.convertToByteArray(id);
- }
-
- public HeaderSet delete(HeaderSet header) throws IOException {
-
- Operation op = put(header);
- op.getResponseCode();
- HeaderSet returnValue = op.getReceivedHeader();
- op.close();
-
- return returnValue;
- }
-
- public HeaderSet disconnect(HeaderSet header) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
- // Determine the header byte array
- byte[] head = null;
- if (header != null) {
- if (header.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
- }
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- header.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, header.mConnectionID, 0, 4);
- }
- head = ObexHelper.createHeader(header, false);
-
- if ((head.length + 3) > mMaxTxPacketSize) {
- throw new IOException("Packet size exceeds max packet size");
- }
- } else {
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- head = new byte[5];
- head[0] = (byte)HeaderSet.CONNECTION_ID;
- System.arraycopy(mConnectionId, 0, head, 1, 4);
- }
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
-
- /*
- * An OBEX DISCONNECT reply from the server:
- * Byte 1: Response code
- * Bytes 2 & 3: packet size
- * Bytes 4 & up: headers
- */
-
- /* response code , and header are ignored
- * */
-
- synchronized (this) {
- mObexConnected = false;
- setRequestInactive();
- }
-
- return returnHeaderSet;
- }
-
- public long getConnectionID() {
-
- if (mConnectionId == null) {
- return -1;
- }
- return ObexHelper.convertToLong(mConnectionId);
- }
-
- public Operation put(HeaderSet header) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
- HeaderSet head;
- if (header == null) {
- head = new HeaderSet();
- } else {
- head = header;
- // when auth is initiated by client ,save the digest
- if (head.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
-
- // Add the connection ID if one exists
- if (mConnectionId != null) {
-
- head.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
- }
-
- if(mLocalSrmSupported) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
- /* TODO: Consider creating an interface to get the wait state.
- * On an android system, I cannot see when this is to be used.
- if(getLocalWaitState()) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
- }
- */
- }
- return new ClientOperation(mMaxTxPacketSize, this, head, false);
- }
-
- public void setAuthenticator(Authenticator auth) throws IOException {
- if (auth == null) {
- throw new IOException("Authenticator may not be null");
- }
- mAuthenticator = auth;
- }
-
- public HeaderSet setPath(HeaderSet header, boolean backup, boolean create) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
- ensureOpen();
-
- int totalLength = 2;
- byte[] head = null;
- HeaderSet headset;
- if (header == null) {
- headset = new HeaderSet();
- } else {
- headset = header;
- if (headset.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
-
- // when auth is initiated by client ,save the digest
- if (headset.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
- }
-
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- headset.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, headset.mConnectionID, 0, 4);
- }
-
- head = ObexHelper.createHeader(headset, false);
- totalLength += head.length;
-
- if (totalLength > mMaxTxPacketSize) {
- throw new IOException("Packet size exceeds max packet size");
- }
-
- int flags = 0;
- /*
- * The backup flag bit is bit 0 so if we add 1, this will set that bit
- */
- if (backup) {
- flags++;
- }
- /*
- * The create bit is bit 1 so if we or with 2 the bit will be set.
- */
- if (!create) {
- flags |= 2;
- }
-
- /*
- * An OBEX SETPATH packet to the server:
- * Byte 1: 0x85
- * Byte 2 & 3: packet size
- * Byte 4: flags
- * Byte 5: constants
- * Byte 6 & up: headers
- */
- byte[] packet = new byte[totalLength];
- packet[0] = (byte)flags;
- packet[1] = (byte)0x00;
- if (headset != null) {
- System.arraycopy(head, 0, packet, 2, head.length);
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
-
- /*
- * An OBEX SETPATH reply from the server:
- * Byte 1: Response code
- * Bytes 2 & 3: packet size
- * Bytes 4 & up: headers
- */
-
- setRequestInactive();
-
- return returnHeaderSet;
- }
-
- /**
- * Verifies that the connection is open.
- * @throws IOException if the connection is closed
- */
- public synchronized void ensureOpen() throws IOException {
- if (!mOpen) {
- throw new IOException("Connection closed");
- }
- }
-
- /**
- * Set request inactive. Allows Put and get operation objects to tell this
- * object when they are done.
- */
- /*package*/synchronized void setRequestInactive() {
- mRequestActive = false;
- }
-
- /**
- * Set request to active.
- * @throws IOException if already active
- */
- private synchronized void setRequestActive() throws IOException {
- if (mRequestActive) {
- throw new IOException("OBEX request is already being performed");
- }
- mRequestActive = true;
- }
-
- /**
- * Sends a standard request to the client. It will then wait for the reply
- * and update the header set object provided. If any authentication headers
- * (i.e. authentication challenge or authentication response) are received,
- * they will be processed.
- * @param opCode the type of request to send to the client
- * @param head the headers to send to the client
- * @param header the header object to update with the response
- * @param privateInput the input stream used by the Operation object; null
- * if this is called on a CONNECT, SETPATH or DISCONNECT
- * @return
- * <code>true</code> if the operation completed successfully;
- * <code>false</code> if an authentication response failed to pass
- * @throws IOException if an IO error occurs
- */
- public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
- PrivateInputStream privateInput, boolean srmActive) throws IOException {
- //check header length with local max size
- if (head != null) {
- if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
- // TODO: This is an implementation limit - not a specification requirement.
- throw new IOException("header too large ");
- }
- }
-
- boolean skipSend = false;
- boolean skipReceive = false;
- if (srmActive == true) {
- if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
- // we are in the middle of a SRM PUT operation, don't expect a continue.
- skipReceive = true;
- } else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
- // We are still sending the get request, send, but don't expect continue
- // until the request is transfered (the final bit is set)
- skipReceive = true;
- } else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- // All done sending the request, expect data from the server, without
- // sending continue.
- skipSend = true;
- }
-
- }
-
- int bytesReceived;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- out.write((byte)opCode);
-
- // Determine if there are any headers to send
- if (head == null) {
- out.write(0x00);
- out.write(0x03);
- } else {
- out.write((byte)((head.length + 3) >> 8));
- out.write((byte)(head.length + 3));
- out.write(head);
- }
-
- if (!skipSend) {
- // Write the request to the output stream and flush the stream
- mOutput.write(out.toByteArray());
- // TODO: is this really needed? if this flush is implemented
- // correctly, we will get a gap between each obex packet.
- // which is kind of the idea behind SRM to avoid.
- // Consider offloading to another thread (async action)
- mOutput.flush();
- }
-
- if (!skipReceive) {
- header.responseCode = mInput.read();
-
- int length = ((mInput.read() << 8) | (mInput.read()));
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- throw new IOException("Packet received exceeds packet size limit");
- }
- if (length > ObexHelper.BASE_PACKET_LENGTH) {
- byte[] data = null;
- if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
- @SuppressWarnings("unused")
- int version = mInput.read();
- @SuppressWarnings("unused")
- int flags = mInput.read();
- mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
-
- //check with local max size
- if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
- mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
- }
-
- // check with transport maximum size
- if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
- // To increase this size, increase the buffer size in L2CAP layer
- // in Bluedroid.
- Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
- + " requested. Transport only allows: "
- + ObexHelper.getMaxTxPacketSize(mTransport)
- + " Lowering limit to this value.");
- mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
- }
-
- if (length > 7) {
- data = new byte[length - 7];
-
- bytesReceived = mInput.read(data);
- while (bytesReceived != (length - 7)) {
- bytesReceived += mInput.read(data, bytesReceived, data.length
- - bytesReceived);
- }
- } else {
- return true;
- }
- } else {
- data = new byte[length - 3];
- bytesReceived = mInput.read(data);
-
- while (bytesReceived != (length - 3)) {
- bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
- }
- if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
- return true;
- }
- }
-
- byte[] body = ObexHelper.updateHeaderSet(header, data);
- if ((privateInput != null) && (body != null)) {
- privateInput.writeBytes(body, 1);
- }
-
- if (header.mConnectionID != null) {
- mConnectionId = new byte[4];
- System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
- }
-
- if (header.mAuthResp != null) {
- if (!handleAuthResp(header.mAuthResp)) {
- setRequestInactive();
- throw new IOException("Authentication Failed");
- }
- }
-
- if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
- && (header.mAuthChall != null)) {
-
- if (handleAuthChall(header)) {
- out.write((byte)HeaderSet.AUTH_RESPONSE);
- out.write((byte)((header.mAuthResp.length + 3) >> 8));
- out.write((byte)(header.mAuthResp.length + 3));
- out.write(header.mAuthResp);
- header.mAuthChall = null;
- header.mAuthResp = null;
-
- byte[] sendHeaders = new byte[out.size() - 3];
- System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
-
- return sendRequest(opCode, sendHeaders, header, privateInput, false);
- }
- }
- }
- }
-
- return true;
- }
-
- public void close() throws IOException {
- mOpen = false;
- mInput.close();
- mOutput.close();
- }
-
- public boolean isSrmSupported() {
- return mLocalSrmSupported;
- }
-}
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
deleted file mode 100644
index 35fe1863e1fb..000000000000
--- a/obex/javax/obex/HeaderSet.java
+++ /dev/null
@@ -1,710 +0,0 @@
-/*
- * Copyright (c) 2014 The Android Open Source Project
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Calendar;
-import java.security.SecureRandom;
-
-/**
- * This class implements the javax.obex.HeaderSet interface for OBEX over
- * RFCOMM or OBEX over l2cap.
- * @hide
- */
-public final class HeaderSet {
-
- /**
- * Represents the OBEX Count header. This allows the connection statement to
- * tell the server how many objects it plans to send or retrieve.
- * <P>
- * The value of <code>COUNT</code> is 0xC0 (192).
- */
- public static final int COUNT = 0xC0;
-
- /**
- * Represents the OBEX Name header. This specifies the name of the object.
- * <P>
- * The value of <code>NAME</code> is 0x01 (1).
- */
- public static final int NAME = 0x01;
-
- /**
- * Represents the OBEX Type header. This allows a request to specify the
- * type of the object (e.g. text, html, binary, etc.).
- * <P>
- * The value of <code>TYPE</code> is 0x42 (66).
- */
- public static final int TYPE = 0x42;
-
- /**
- * Represents the OBEX Length header. This is the length of the object in
- * bytes.
- * <P>
- * The value of <code>LENGTH</code> is 0xC3 (195).
- */
- public static final int LENGTH = 0xC3;
-
- /**
- * Represents the OBEX Time header using the ISO 8601 standards. This is the
- * preferred time header.
- * <P>
- * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
- */
- public static final int TIME_ISO_8601 = 0x44;
-
- /**
- * Represents the OBEX Time header using the 4 byte representation. This is
- * only included for backwards compatibility. It represents the number of
- * seconds since January 1, 1970.
- * <P>
- * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
- */
- public static final int TIME_4_BYTE = 0xC4;
-
- /**
- * Represents the OBEX Description header. This is a text description of the
- * object.
- * <P>
- * The value of <code>DESCRIPTION</code> is 0x05 (5).
- */
- public static final int DESCRIPTION = 0x05;
-
- /**
- * Represents the OBEX Target header. This is the name of the service an
- * operation is targeted to.
- * <P>
- * The value of <code>TARGET</code> is 0x46 (70).
- */
- public static final int TARGET = 0x46;
-
- /**
- * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
- * included in a request or reply.
- * <P>
- * The value of <code>HTTP</code> is 0x47 (71).
- */
- public static final int HTTP = 0x47;
-
- /**
- * Represents the OBEX BODY header.
- * <P>
- * The value of <code>BODY</code> is 0x48 (72).
- */
- public static final int BODY = 0x48;
-
- /**
- * Represents the OBEX End of BODY header.
- * <P>
- * The value of <code>BODY</code> is 0x49 (73).
- */
- public static final int END_OF_BODY = 0x49;
-
- /**
- * Represents the OBEX Who header. Identifies the OBEX application to
- * determine if the two peers are talking to each other.
- * <P>
- * The value of <code>WHO</code> is 0x4A (74).
- */
- public static final int WHO = 0x4A;
-
- /**
- * Represents the OBEX Connection ID header. Identifies used for OBEX
- * connection multiplexing.
- * <P>
- * The value of <code>CONNECTION_ID</code> is 0xCB (203).
- */
-
- public static final int CONNECTION_ID = 0xCB;
-
- /**
- * Represents the OBEX Application Parameter header. This header specifies
- * additional application request and response information.
- * <P>
- * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
- */
- public static final int APPLICATION_PARAMETER = 0x4C;
-
- /**
- * Represents the OBEX authentication digest-challenge.
- * <P>
- * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
- */
- public static final int AUTH_CHALLENGE = 0x4D;
-
- /**
- * Represents the OBEX authentication digest-response.
- * <P>
- * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
- */
- public static final int AUTH_RESPONSE = 0x4E;
-
- /**
- * Represents the OBEX Object Class header. This header specifies the OBEX
- * object class of the object.
- * <P>
- * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
- */
- public static final int OBJECT_CLASS = 0x4F;
-
- /**
- * Represents the OBEX Single Response Mode (SRM). This header is used
- * for Single response mode, introduced in OBEX 1.5.
- * <P>
- * The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151).
- */
- public static final int SINGLE_RESPONSE_MODE = 0x97;
-
- /**
- * Represents the OBEX Single Response Mode Parameters. This header is used
- * for Single response mode, introduced in OBEX 1.5.
- * <P>
- * The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152).
- */
- public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98;
-
- private Long mCount; // 4 byte unsigned integer
-
- private String mName; // null terminated Unicode text string
-
- private boolean mEmptyName;
-
- private String mType; // null terminated ASCII text string
-
- private Long mLength; // 4 byte unsigend integer
-
- private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
-
- private Calendar mByteTime; // 4 byte unsigned integer
-
- private String mDescription; // null terminated Unicode text String
-
- private byte[] mTarget; // byte sequence
-
- private byte[] mHttpHeader; // byte sequence
-
- private byte[] mWho; // length prefixed byte sequence
-
- private byte[] mAppParam; // byte sequence of the form tag length value
-
- private byte[] mObjectClass; // byte sequence
-
- private String[] mUnicodeUserDefined; // null terminated unicode string
-
- private byte[][] mSequenceUserDefined; // byte sequence user defined
-
- private Byte[] mByteUserDefined; // 1 byte
-
- private Long[] mIntegerUserDefined; // 4 byte unsigned integer
-
- private SecureRandom mRandom = null;
-
- private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM
-
- private Byte mSrmParam; // byte representing the SRM parameters - only "wait"
- // is supported by Bluetooth
-
- /*package*/ byte[] nonce;
-
- public byte[] mAuthChall; // The authentication challenge header
-
- public byte[] mAuthResp; // The authentication response header
-
- public byte[] mConnectionID; // THe connection ID
-
- public int responseCode;
-
- /**
- * Creates new <code>HeaderSet</code> object.
- * @param size the max packet size for this connection
- */
- public HeaderSet() {
- mUnicodeUserDefined = new String[16];
- mSequenceUserDefined = new byte[16][];
- mByteUserDefined = new Byte[16];
- mIntegerUserDefined = new Long[16];
- responseCode = -1;
- }
-
- /**
- * Sets flag for special "value" of NAME header which should be empty. This
- * is not the same as NAME header with empty string in which case it will
- * have length of 5 bytes. It should be 3 bytes with only header id and
- * length field.
- */
- public void setEmptyNameHeader() {
- mName = null;
- mEmptyName = true;
- }
-
- /**
- * Gets flag for special "value" of NAME header which should be empty. See
- * above.
- */
- public boolean getEmptyNameHeader() {
- return mEmptyName;
- }
-
- /**
- * Sets the value of the header identifier to the value provided. The type
- * of object must correspond to the Java type defined in the description of
- * this interface. If <code>null</code> is passed as the
- * <code>headerValue</code> then the header will be removed from the set of
- * headers to include in the next request.
- * @param headerID the identifier to include in the message
- * @param headerValue the value of the header identifier
- * @throws IllegalArgumentException if the header identifier provided is not
- * one defined in this interface or a user-defined header; if the
- * type of <code>headerValue</code> is not the correct Java type as
- * defined in the description of this interface\
- */
- public void setHeader(int headerID, Object headerValue) {
- long temp = -1;
-
- switch (headerID) {
- case COUNT:
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mCount = null;
- break;
- }
- throw new IllegalArgumentException("Count must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
- }
- mCount = (Long)headerValue;
- break;
- case NAME:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Name must be a String");
- }
- mEmptyName = false;
- mName = (String)headerValue;
- break;
- case TYPE:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Type must be a String");
- }
- mType = (String)headerValue;
- break;
- case LENGTH:
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mLength = null;
- break;
- }
- throw new IllegalArgumentException("Length must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
- }
- mLength = (Long)headerValue;
- break;
- case TIME_ISO_8601:
- if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
- throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
- }
- mIsoTime = (Calendar)headerValue;
- break;
- case TIME_4_BYTE:
- if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
- throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
- }
- mByteTime = (Calendar)headerValue;
- break;
- case DESCRIPTION:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Description must be a String");
- }
- mDescription = (String)headerValue;
- break;
- case TARGET:
- if (headerValue == null) {
- mTarget = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("Target must be a byte array");
- } else {
- mTarget = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
- }
- }
- break;
- case HTTP:
- if (headerValue == null) {
- mHttpHeader = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("HTTP must be a byte array");
- } else {
- mHttpHeader = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
- }
- }
- break;
- case WHO:
- if (headerValue == null) {
- mWho = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("WHO must be a byte array");
- } else {
- mWho = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
- }
- }
- break;
- case OBJECT_CLASS:
- if (headerValue == null) {
- mObjectClass = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("Object Class must be a byte array");
- } else {
- mObjectClass = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
- }
- }
- break;
- case APPLICATION_PARAMETER:
- if (headerValue == null) {
- mAppParam = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException(
- "Application Parameter must be a byte array");
- } else {
- mAppParam = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
- }
- }
- break;
- case SINGLE_RESPONSE_MODE:
- if (headerValue == null) {
- mSingleResponseMode = null;
- } else {
- if (!(headerValue instanceof Byte)) {
- throw new IllegalArgumentException(
- "Single Response Mode must be a Byte");
- } else {
- mSingleResponseMode = (Byte)headerValue;
- }
- }
- break;
- case SINGLE_RESPONSE_MODE_PARAMETER:
- if (headerValue == null) {
- mSrmParam = null;
- } else {
- if (!(headerValue instanceof Byte)) {
- throw new IllegalArgumentException(
- "Single Response Mode Parameter must be a Byte");
- } else {
- mSrmParam = (Byte)headerValue;
- }
- }
- break;
- default:
- // Verify that it was not a Unicode String user Defined
- if ((headerID >= 0x30) && (headerID <= 0x3F)) {
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException(
- "Unicode String User Defined must be a String");
- }
- mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
-
- break;
- }
- // Verify that it was not a byte sequence user defined value
- if ((headerID >= 0x70) && (headerID <= 0x7F)) {
-
- if (headerValue == null) {
- mSequenceUserDefined[headerID - 0x70] = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException(
- "Byte Sequence User Defined must be a byte array");
- } else {
- mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
- 0, mSequenceUserDefined[headerID - 0x70].length);
- }
- }
- break;
- }
- // Verify that it was not a Byte user Defined
- if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
- if ((headerValue != null) && (!(headerValue instanceof Byte))) {
- throw new IllegalArgumentException("ByteUser Defined must be a Byte");
- }
- mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
-
- break;
- }
- // Verify that is was not the 4 byte unsigned integer user
- // defined header
- if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mIntegerUserDefined[headerID - 0xF0] = null;
- break;
- }
- throw new IllegalArgumentException("Integer User Defined must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException(
- "Integer User Defined must be between 0 and 0xFFFFFFFF");
- }
- mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
- break;
- }
- throw new IllegalArgumentException("Invalid Header Identifier");
- }
- }
-
- /**
- * Retrieves the value of the header identifier provided. The type of the
- * Object returned is defined in the description of this interface.
- * @param headerID the header identifier whose value is to be returned
- * @return the value of the header provided or <code>null</code> if the
- * header identifier specified is not part of this
- * <code>HeaderSet</code> object
- * @throws IllegalArgumentException if the <code>headerID</code> is not one
- * defined in this interface or any of the user-defined headers
- * @throws IOException if an error occurred in the transport layer during
- * the operation or if the connection has been closed
- */
- public Object getHeader(int headerID) throws IOException {
-
- switch (headerID) {
- case COUNT:
- return mCount;
- case NAME:
- return mName;
- case TYPE:
- return mType;
- case LENGTH:
- return mLength;
- case TIME_ISO_8601:
- return mIsoTime;
- case TIME_4_BYTE:
- return mByteTime;
- case DESCRIPTION:
- return mDescription;
- case TARGET:
- return mTarget;
- case HTTP:
- return mHttpHeader;
- case WHO:
- return mWho;
- case CONNECTION_ID:
- return mConnectionID;
- case OBJECT_CLASS:
- return mObjectClass;
- case APPLICATION_PARAMETER:
- return mAppParam;
- case SINGLE_RESPONSE_MODE:
- return mSingleResponseMode;
- case SINGLE_RESPONSE_MODE_PARAMETER:
- return mSrmParam;
- default:
- // Verify that it was not a Unicode String user Defined
- if ((headerID >= 0x30) && (headerID <= 0x3F)) {
- return mUnicodeUserDefined[headerID - 0x30];
- }
- // Verify that it was not a byte sequence user defined header
- if ((headerID >= 0x70) && (headerID <= 0x7F)) {
- return mSequenceUserDefined[headerID - 0x70];
- }
- // Verify that it was not a byte user defined header
- if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
- return mByteUserDefined[headerID - 0xB0];
- }
- // Verify that it was not a integer user defined header
- if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
- return mIntegerUserDefined[headerID - 0xF0];
- }
- throw new IllegalArgumentException("Invalid Header Identifier");
- }
- }
-
- /**
- * Retrieves the list of headers that may be retrieved via the
- * <code>getHeader</code> method that will not return <code>null</code>. In
- * other words, this method returns all the headers that are available in
- * this object.
- * @see #getHeader
- * @return the array of headers that are set in this object or
- * <code>null</code> if no headers are available
- * @throws IOException if an error occurred in the transport layer during
- * the operation or the connection has been closed
- */
- public int[] getHeaderList() throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- if (mCount != null) {
- out.write(COUNT);
- }
- if (mName != null) {
- out.write(NAME);
- }
- if (mType != null) {
- out.write(TYPE);
- }
- if (mLength != null) {
- out.write(LENGTH);
- }
- if (mIsoTime != null) {
- out.write(TIME_ISO_8601);
- }
- if (mByteTime != null) {
- out.write(TIME_4_BYTE);
- }
- if (mDescription != null) {
- out.write(DESCRIPTION);
- }
- if (mTarget != null) {
- out.write(TARGET);
- }
- if (mHttpHeader != null) {
- out.write(HTTP);
- }
- if (mWho != null) {
- out.write(WHO);
- }
- if (mAppParam != null) {
- out.write(APPLICATION_PARAMETER);
- }
- if (mObjectClass != null) {
- out.write(OBJECT_CLASS);
- }
- if(mSingleResponseMode != null) {
- out.write(SINGLE_RESPONSE_MODE);
- }
- if(mSrmParam != null) {
- out.write(SINGLE_RESPONSE_MODE_PARAMETER);
- }
-
- for (int i = 0x30; i < 0x40; i++) {
- if (mUnicodeUserDefined[i - 0x30] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0x70; i < 0x80; i++) {
- if (mSequenceUserDefined[i - 0x70] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0xB0; i < 0xC0; i++) {
- if (mByteUserDefined[i - 0xB0] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0xF0; i < 0x100; i++) {
- if (mIntegerUserDefined[i - 0xF0] != null) {
- out.write(i);
- }
- }
-
- byte[] headers = out.toByteArray();
- out.close();
-
- if ((headers == null) || (headers.length == 0)) {
- return null;
- }
-
- int[] result = new int[headers.length];
- for (int i = 0; i < headers.length; i++) {
- // Convert the byte to a positive integer. That is, an integer
- // between 0 and 256.
- result[i] = headers[i] & 0xFF;
- }
-
- return result;
- }
-
- /**
- * Sets the authentication challenge header. The <code>realm</code> will be
- * encoded based upon the default encoding scheme used by the implementation
- * to encode strings. Therefore, the encoding scheme used to encode the
- * <code>realm</code> is application dependent.
- * @param realm a short description that describes what password to use; if
- * <code>null</code> no realm will be sent in the authentication
- * challenge header
- * @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- * @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read-only access will be
- * granted if successful
- * @throws IOException
- */
- public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
- throws IOException {
-
- nonce = new byte[16];
- if(mRandom == null) {
- mRandom = new SecureRandom();
- }
- for (int i = 0; i < 16; i++) {
- nonce[i] = (byte)mRandom.nextInt();
- }
-
- mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
- }
-
- /**
- * Returns the response code received from the server. Response codes are
- * defined in the <code>ResponseCodes</code> class.
- * @see ResponseCodes
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet()</code> in a <code>ClientSession</code>
- * object; if this object was created by an OBEX server
- */
- public int getResponseCode() throws IOException {
- if (responseCode == -1) {
- throw new IOException("May not be called on a server");
- } else {
- return responseCode;
- }
- }
-}
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
deleted file mode 100644
index 843793ad98f8..000000000000
--- a/obex/javax/obex/ObexHelper.java
+++ /dev/null
@@ -1,1100 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
-
-
-/**
- * This class defines a set of helper methods for the implementation of Obex.
- * @hide
- */
-public final class ObexHelper {
-
- private static final String TAG = "ObexHelper";
- public static final boolean VDBG = false;
- /**
- * Defines the basic packet length used by OBEX. Every OBEX packet has the
- * same basic format:<BR>
- * Byte 0: Request or Response Code Byte 1&2: Length of the packet.
- */
- public static final int BASE_PACKET_LENGTH = 3;
-
- /** Prevent object construction of helper class */
- private ObexHelper() {
- }
-
- /**
- * The maximum packet size for OBEX packets that this client can handle. At
- * present, this must be changed for each port. TODO: The max packet size
- * should be the Max incoming MTU minus TODO: L2CAP package headers and
- * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
- * LocalDevice.getProperty().
- * NOTE: This value must be larger than or equal to the L2CAP SDU
- */
- /*
- * android note set as 0xFFFE to match remote MPS
- */
- public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
-
- // The minimum allowed max packet size is 255 according to the OBEX specification
- public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
-
- // The length of OBEX Byte Sequency Header Id according to the OBEX specification
- public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
-
- /**
- * Temporary workaround to be able to push files to Windows 7.
- * TODO: Should be removed as soon as Microsoft updates their driver.
- */
- public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00;
-
- public static final int OBEX_OPCODE_FINAL_BIT_MASK = 0x80;
-
- public static final int OBEX_OPCODE_CONNECT = 0x80;
-
- public static final int OBEX_OPCODE_DISCONNECT = 0x81;
-
- public static final int OBEX_OPCODE_PUT = 0x02;
-
- public static final int OBEX_OPCODE_PUT_FINAL = 0x82;
-
- public static final int OBEX_OPCODE_GET = 0x03;
-
- public static final int OBEX_OPCODE_GET_FINAL = 0x83;
-
- public static final int OBEX_OPCODE_RESERVED = 0x04;
-
- public static final int OBEX_OPCODE_RESERVED_FINAL = 0x84;
-
- public static final int OBEX_OPCODE_SETPATH = 0x85;
-
- public static final int OBEX_OPCODE_ABORT = 0xFF;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ASCII = 0x00;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_1 = 0x01;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_2 = 0x02;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_3 = 0x03;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_4 = 0x04;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_5 = 0x05;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_6 = 0x06;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_7 = 0x07;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_8 = 0x08;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_9 = 0x09;
-
- public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
-
- public static final byte OBEX_SRM_ENABLE = 0x01; // For BT we only need enable/disable
- public static final byte OBEX_SRM_DISABLE = 0x00;
- public static final byte OBEX_SRM_SUPPORT = 0x02; // Unused for now
-
- public static final byte OBEX_SRMP_WAIT = 0x01; // Only SRMP value used by BT
-
- /**
- * Updates the HeaderSet with the headers received in the byte array
- * provided. Invalid headers are ignored.
- * <P>
- * The first two bits of an OBEX Header specifies the type of object that is
- * being sent. The table below specifies the meaning of the high bits.
- * <TABLE>
- * <TR>
- * <TH>Bits 8 and 7</TH>
- * <TH>Value</TH>
- * <TH>Description</TH>
- * </TR>
- * <TR>
- * <TD>00</TD>
- * <TD>0x00</TD>
- * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD>
- * </TR>
- * <TR>
- * <TD>01</TD>
- * <TD>0x40</TD>
- * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD>
- * </TR>
- * <TR>
- * <TD>10</TD>
- * <TD>0x80</TD>
- * <TD>1 byte quantity</TD>
- * </TR>
- * <TR>
- * <TD>11</TD>
- * <TD>0xC0</TD>
- * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD>
- * </TR>
- * </TABLE>
- * This method uses the information in this table to determine the type of
- * Java object to create and passes that object with the full header to
- * setHeader() to update the HeaderSet object. Invalid headers will cause an
- * exception to be thrown. When it is thrown, it is ignored.
- * @param header the HeaderSet to update
- * @param headerArray the byte array containing headers
- * @return the result of the last start body or end body header provided;
- * the first byte in the result will specify if a body or end of
- * body is received
- * @throws IOException if an invalid header was found
- */
- public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
- int index = 0;
- int length = 0;
- int headerID;
- byte[] value = null;
- byte[] body = null;
- HeaderSet headerImpl = header;
- try {
- while (index < headerArray.length) {
- headerID = 0xFF & headerArray[index];
- switch (headerID & (0xC0)) {
-
- /*
- * 0x00 is a unicode null terminate string with the first
- * two bytes after the header identifier being the length
- */
- case 0x00:
- // Fall through
- /*
- * 0x40 is a byte sequence with the first
- * two bytes after the header identifier being the length
- */
- case 0x40:
- boolean trimTail = true;
- index++;
- length = ((0xFF & headerArray[index]) << 8) +
- (0xFF & headerArray[index + 1]);
- index += 2;
- if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
- Log.e(TAG, "Remote sent an OBEX packet with " +
- "incorrect header length = " + length);
- break;
- }
- length -= OBEX_BYTE_SEQ_HEADER_LEN;
- value = new byte[length];
- System.arraycopy(headerArray, index, value, 0, length);
- if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
- trimTail = false;
- }
- switch (headerID) {
- case HeaderSet.TYPE:
- try {
- // Remove trailing null
- if (trimTail == false) {
- headerImpl.setHeader(headerID, new String(value, 0,
- value.length, "ISO8859_1"));
- } else {
- headerImpl.setHeader(headerID, new String(value, 0,
- value.length - 1, "ISO8859_1"));
- }
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
- break;
-
- case HeaderSet.AUTH_CHALLENGE:
- headerImpl.mAuthChall = new byte[length];
- System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0,
- length);
- break;
-
- case HeaderSet.AUTH_RESPONSE:
- headerImpl.mAuthResp = new byte[length];
- System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0,
- length);
- break;
-
- case HeaderSet.BODY:
- /* Fall Through */
- case HeaderSet.END_OF_BODY:
- body = new byte[length + 1];
- body[0] = (byte)headerID;
- System.arraycopy(headerArray, index, body, 1, length);
- break;
-
- case HeaderSet.TIME_ISO_8601:
- try {
- String dateString = new String(value, "ISO8859_1");
- Calendar temp = Calendar.getInstance();
- if ((dateString.length() == 16)
- && (dateString.charAt(15) == 'Z')) {
- temp.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
- temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
- 0, 4)));
- temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
- 4, 6)));
- temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
- .substring(6, 8)));
- temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
- .substring(9, 11)));
- temp.set(Calendar.MINUTE, Integer.parseInt(dateString
- .substring(11, 13)));
- temp.set(Calendar.SECOND, Integer.parseInt(dateString
- .substring(13, 15)));
- headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
- break;
-
- default:
- if ((headerID & 0xC0) == 0x00) {
- headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
- value, true));
- } else {
- headerImpl.setHeader(headerID, value);
- }
- }
-
- index += length;
- break;
-
- /*
- * 0x80 is a byte header. The only valid byte headers are
- * the 16 user defined byte headers.
- */
- case 0x80:
- index++;
- try {
- headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
- } catch (Exception e) {
- // Not a valid header so ignore
- }
- index++;
- break;
-
- /*
- * 0xC0 is a 4 byte unsigned integer header and with the
- * exception of TIME_4_BYTE will be converted to a Long
- * and added.
- */
- case 0xC0:
- index++;
- value = new byte[4];
- System.arraycopy(headerArray, index, value, 0, 4);
- try {
- if (headerID != HeaderSet.TIME_4_BYTE) {
- // Determine if it is a connection ID. These
- // need to be handled differently
- if (headerID == HeaderSet.CONNECTION_ID) {
- headerImpl.mConnectionID = new byte[4];
- System.arraycopy(value, 0, headerImpl.mConnectionID, 0, 4);
- } else {
- headerImpl.setHeader(headerID, Long
- .valueOf(convertToLong(value)));
- }
- } else {
- Calendar temp = Calendar.getInstance();
- temp.setTime(new Date(convertToLong(value) * 1000L));
- headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
- }
- } catch (Exception e) {
- // Not a valid header so ignore
- throw new IOException("Header was not formatted properly", e);
- }
- index += 4;
- break;
- }
-
- }
- } catch (IOException e) {
- throw new IOException("Header was not formatted properly", e);
- }
-
- return body;
- }
-
- /**
- * Creates the header part of OBEX packet based on the header provided.
- * TODO: Could use getHeaderList() to get the array of headers to include
- * and then use the high two bits to determine the the type of the object
- * and construct the byte array from that. This will make the size smaller.
- * @param head the header used to construct the byte array
- * @param nullOut <code>true</code> if the header should be set to
- * <code>null</code> once it is added to the array or
- * <code>false</code> if it should not be nulled out
- * @return the header of an OBEX packet
- */
- public static byte[] createHeader(HeaderSet head, boolean nullOut) {
- Long intHeader = null;
- String stringHeader = null;
- Calendar dateHeader = null;
- Byte byteHeader = null;
- StringBuffer buffer = null;
- byte[] value = null;
- byte[] result = null;
- byte[] lengthArray = new byte[2];
- int length;
- HeaderSet headImpl = null;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- headImpl = head;
-
- try {
- /*
- * Determine if there is a connection ID to send. If there is,
- * then it should be the first header in the packet.
- */
- if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
-
- out.write((byte)HeaderSet.CONNECTION_ID);
- out.write(headImpl.mConnectionID);
- }
-
- // Count Header
- intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
- if (intHeader != null) {
- out.write((byte)HeaderSet.COUNT);
- value = ObexHelper.convertToByteArray(intHeader.longValue());
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.COUNT, null);
- }
- }
-
- // Name Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.NAME);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(0xFF & (length >> 8));
- lengthArray[1] = (byte)(0xFF & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.NAME, null);
- }
- } else if (headImpl.getEmptyNameHeader()) {
- out.write((byte) HeaderSet.NAME);
- lengthArray[0] = (byte) 0x00;
- lengthArray[1] = (byte) 0x03;
- out.write(lengthArray);
- }
-
- // Type Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.TYPE);
- try {
- value = stringHeader.getBytes("ISO8859_1");
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
-
- length = value.length + 4;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- out.write(0x00);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TYPE, null);
- }
- }
-
- // Length Header
- intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
- if (intHeader != null) {
- out.write((byte)HeaderSet.LENGTH);
- value = ObexHelper.convertToByteArray(intHeader.longValue());
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.LENGTH, null);
- }
- }
-
- // Time ISO Header
- dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
- if (dateHeader != null) {
-
- /*
- * The ISO Header should take the form YYYYMMDDTHHMMSSZ. The
- * 'Z' will only be included if it is a UTC time.
- */
- buffer = new StringBuffer();
- int temp = dateHeader.get(Calendar.YEAR);
- for (int i = temp; i < 1000; i = i * 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.MONTH);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.DAY_OF_MONTH);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- buffer.append("T");
- temp = dateHeader.get(Calendar.HOUR_OF_DAY);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.MINUTE);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.SECOND);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
-
- if (dateHeader.getTimeZone().getID().equals("UTC")) {
- buffer.append("Z");
- }
-
- try {
- value = buffer.toString().getBytes("ISO8859_1");
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
-
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(HeaderSet.TIME_ISO_8601);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
- }
- }
-
- // Time 4 Byte Header
- dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
- if (dateHeader != null) {
- out.write(HeaderSet.TIME_4_BYTE);
-
- /*
- * Need to call getTime() twice. The first call will return
- * a java.util.Date object. The second call returns the number
- * of milliseconds since January 1, 1970. We need to convert
- * it to seconds since the TIME_4_BYTE expects the number of
- * seconds since January 1, 1970.
- */
- value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
- }
- }
-
- // Description Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.DESCRIPTION);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.DESCRIPTION, null);
- }
- }
-
- // Target Header
- value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
- if (value != null) {
- out.write((byte)HeaderSet.TARGET);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TARGET, null);
- }
- }
-
- // HTTP Header
- value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
- if (value != null) {
- out.write((byte)HeaderSet.HTTP);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.HTTP, null);
- }
- }
-
- // Who Header
- value = (byte[])headImpl.getHeader(HeaderSet.WHO);
- if (value != null) {
- out.write((byte)HeaderSet.WHO);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.WHO, null);
- }
- }
-
- // Connection ID Header
- value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
- if (value != null) {
- out.write((byte)HeaderSet.APPLICATION_PARAMETER);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
- }
- }
-
- // Object Class Header
- value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
- if (value != null) {
- out.write((byte)HeaderSet.OBJECT_CLASS);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
- }
- }
-
- // Check User Defined Headers
- for (int i = 0; i < 16; i++) {
-
- //Unicode String Header
- stringHeader = (String)headImpl.getHeader(i + 0x30);
- if (stringHeader != null) {
- out.write((byte)i + 0x30);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(i + 0x30, null);
- }
- }
-
- // Byte Sequence Header
- value = (byte[])headImpl.getHeader(i + 0x70);
- if (value != null) {
- out.write((byte)i + 0x70);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(i + 0x70, null);
- }
- }
-
- // Byte Header
- byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
- if (byteHeader != null) {
- out.write((byte)i + 0xB0);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(i + 0xB0, null);
- }
- }
-
- // Integer header
- intHeader = (Long)headImpl.getHeader(i + 0xF0);
- if (intHeader != null) {
- out.write((byte)i + 0xF0);
- out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
- if (nullOut) {
- headImpl.setHeader(i + 0xF0, null);
- }
- }
- }
-
- // Add the authentication challenge header
- if (headImpl.mAuthChall != null) {
- out.write((byte)HeaderSet.AUTH_CHALLENGE);
- length = headImpl.mAuthChall.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(headImpl.mAuthChall);
- if (nullOut) {
- headImpl.mAuthChall = null;
- }
- }
-
- // Add the authentication response header
- if (headImpl.mAuthResp != null) {
- out.write((byte)HeaderSet.AUTH_RESPONSE);
- length = headImpl.mAuthResp.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(headImpl.mAuthResp);
- if (nullOut) {
- headImpl.mAuthResp = null;
- }
- }
-
- // TODO:
- // If the SRM and SRMP header is in use, they must be send in the same OBEX packet
- // But the current structure of the obex code cannot handle this, and therefore
- // it makes sense to put them in the tail of the headers, since we then reduce the
- // chance of enabling SRM to soon. The down side is that SRM cannot be used while
- // transferring non-body headers
-
- // Add the SRM header
- byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if (byteHeader != null) {
- out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, null);
- }
- }
-
- // Add the SRM parameter header
- byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if (byteHeader != null) {
- out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- }
-
- } catch (IOException e) {
- } finally {
- result = out.toByteArray();
- try {
- out.close();
- } catch (Exception ex) {
- }
- }
-
- return result;
-
- }
-
- /**
- * Determines where the maximum divide is between headers. This method is
- * used by put and get operations to separate headers to a size that meets
- * the max packet size allowed.
- * @param headerArray the headers to separate
- * @param start the starting index to search
- * @param maxSize the maximum size of a packet
- * @return the index of the end of the header block to send or -1 if the
- * header could not be divided because the header is too large
- */
- public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
-
- int fullLength = 0;
- int lastLength = -1;
- int index = start;
- int length = 0;
-
- // TODO: Ensure SRM and SRMP headers are not split into two OBEX packets
-
- while ((fullLength < maxSize) && (index < headerArray.length)) {
- int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
- lastLength = fullLength;
-
- switch (headerID & (0xC0)) {
-
- case 0x00:
- // Fall through
- case 0x40:
-
- index++;
- length = (headerArray[index] < 0 ? headerArray[index] + 256
- : headerArray[index]);
- length = length << 8;
- index++;
- length += (headerArray[index] < 0 ? headerArray[index] + 256
- : headerArray[index]);
- length -= 3;
- index++;
- index += length;
- fullLength += length + 3;
- break;
-
- case 0x80:
-
- index++;
- index++;
- fullLength += 2;
- break;
-
- case 0xC0:
-
- index += 5;
- fullLength += 5;
- break;
-
- }
-
- }
-
- /*
- * Determine if this is the last header or not
- */
- if (lastLength == 0) {
- /*
- * Since this is the last header, check to see if the size of this
- * header is less then maxSize. If it is, return the length of the
- * header, otherwise return -1. The length of the header is
- * returned since it would be the start of the next header
- */
- if (fullLength < maxSize) {
- return headerArray.length;
- } else {
- return -1;
- }
- } else {
- return lastLength + start;
- }
- }
-
- /**
- * Converts the byte array to a long.
- * @param b the byte array to convert to a long
- * @return the byte array as a long
- */
- public static long convertToLong(byte[] b) {
- long result = 0;
- long value = 0;
- long power = 0;
-
- for (int i = (b.length - 1); i >= 0; i--) {
- value = b[i];
- if (value < 0) {
- value += 256;
- }
-
- result = result | (value << power);
- power += 8;
- }
-
- return result;
- }
-
- /**
- * Converts the long to a 4 byte array. The long must be non negative.
- * @param l the long to convert
- * @return a byte array that is the same as the long
- */
- public static byte[] convertToByteArray(long l) {
- byte[] b = new byte[4];
-
- b[0] = (byte)(255 & (l >> 24));
- b[1] = (byte)(255 & (l >> 16));
- b[2] = (byte)(255 & (l >> 8));
- b[3] = (byte)(255 & l);
-
- return b;
- }
-
- /**
- * Converts the String to a UNICODE byte array. It will also add the ending
- * null characters to the end of the string.
- * @param s the string to convert
- * @return the unicode byte array of the string
- */
- public static byte[] convertToUnicodeByteArray(String s) {
- if (s == null) {
- return null;
- }
-
- char c[] = s.toCharArray();
- byte[] result = new byte[(c.length * 2) + 2];
- for (int i = 0; i < c.length; i++) {
- result[(i * 2)] = (byte)(c[i] >> 8);
- result[((i * 2) + 1)] = (byte)c[i];
- }
-
- // Add the UNICODE null character
- result[result.length - 2] = 0;
- result[result.length - 1] = 0;
-
- return result;
- }
-
- /**
- * Retrieves the value from the byte array for the tag value specified. The
- * array should be of the form Tag - Length - Value triplet.
- * @param tag the tag to retrieve from the byte array
- * @param triplet the byte sequence containing the tag length value form
- * @return the value of the specified tag
- */
- public static byte[] getTagValue(byte tag, byte[] triplet) {
-
- int index = findTag(tag, triplet);
- if (index == -1) {
- return null;
- }
-
- index++;
- int length = triplet[index] & 0xFF;
-
- byte[] result = new byte[length];
- index++;
- System.arraycopy(triplet, index, result, 0, length);
-
- return result;
- }
-
- /**
- * Finds the index that starts the tag value pair in the byte array provide.
- * @param tag the tag to look for
- * @param value the byte array to search
- * @return the starting index of the tag or -1 if the tag could not be found
- */
- public static int findTag(byte tag, byte[] value) {
- int length = 0;
-
- if (value == null) {
- return -1;
- }
-
- int index = 0;
-
- while ((index < value.length) && (value[index] != tag)) {
- length = value[index + 1] & 0xFF;
- index += length + 2;
- }
-
- if (index >= value.length) {
- return -1;
- }
-
- return index;
- }
-
- /**
- * Converts the byte array provided to a unicode string.
- * @param b the byte array to convert to a string
- * @param includesNull determine if the byte string provided contains the
- * UNICODE null character at the end or not; if it does, it will be
- * removed
- * @return a Unicode string
- * @throws IllegalArgumentException if the byte array has an odd length
- */
- public static String convertToUnicode(byte[] b, boolean includesNull) {
- if (b == null || b.length == 0) {
- return null;
- }
- int arrayLength = b.length;
- if (!((arrayLength % 2) == 0)) {
- throw new IllegalArgumentException("Byte array not of a valid form");
- }
- arrayLength = (arrayLength >> 1);
- if (includesNull) {
- arrayLength -= 1;
- }
-
- char[] c = new char[arrayLength];
- for (int i = 0; i < arrayLength; i++) {
- int upper = b[2 * i];
- int lower = b[(2 * i) + 1];
- if (upper < 0) {
- upper += 256;
- }
- if (lower < 0) {
- lower += 256;
- }
- // If upper and lower both equal 0, it should be the end of string.
- // Ignore left bytes from array to avoid potential issues
- if (upper == 0 && lower == 0) {
- return new String(c, 0, i);
- }
-
- c[i] = (char)((upper << 8) | lower);
- }
-
- return new String(c);
- }
-
- /**
- * Compute the MD5 hash of the byte array provided. Does not accumulate
- * input.
- * @param in the byte array to hash
- * @return the MD5 hash of the byte array
- */
- public static byte[] computeMd5Hash(byte[] in) {
- try {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- return md5.digest(in);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Computes an authentication challenge header.
- * @param nonce the challenge that will be provided to the peer; the
- * challenge must be 16 bytes long
- * @param realm a short description that describes what password to use
- * @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read only access will be
- * granted if successful
- * @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- * @throws IllegalArgumentException if the challenge is not 16 bytes long;
- * if the realm can not be encoded in less then 255 bytes
- * @throws IOException if the encoding scheme ISO 8859-1 is not supported
- */
- public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
- boolean userID) throws IOException {
- byte[] authChall = null;
-
- if (nonce.length != 16) {
- throw new IllegalArgumentException("Nonce must be 16 bytes long");
- }
-
- /*
- * The authentication challenge is a byte sequence of the following form
- * byte 0: 0x00 - the tag for the challenge
- * byte 1: 0x10 - the length of the challenge; must be 16
- * byte 2-17: the authentication challenge
- * byte 18: 0x01 - the options tag; this is optional in the spec, but
- * we are going to include it in every message
- * byte 19: 0x01 - length of the options; must be 1
- * byte 20: the value of the options; bit 0 is set if user ID is
- * required; bit 1 is set if access mode is read only
- * byte 21: 0x02 - the tag for authentication realm; only included if
- * an authentication realm is specified
- * byte 22: the length of the authentication realm; only included if
- * the authentication realm is specified
- * byte 23: the encoding scheme of the authentication realm; we will use
- * the ISO 8859-1 encoding scheme since it is part of the KVM
- * byte 24 & up: the realm if one is specified.
- */
- if (realm == null) {
- authChall = new byte[21];
- } else {
- if (realm.length() >= 255) {
- throw new IllegalArgumentException("Realm must be less then 255 bytes");
- }
- authChall = new byte[24 + realm.length()];
- authChall[21] = 0x02;
- authChall[22] = (byte)(realm.length() + 1);
- authChall[23] = 0x01; // ISO 8859-1 Encoding
- System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
- }
-
- // Include the nonce field in the header
- authChall[0] = 0x00;
- authChall[1] = 0x10;
- System.arraycopy(nonce, 0, authChall, 2, 16);
-
- // Include the options header
- authChall[18] = 0x01;
- authChall[19] = 0x01;
- authChall[20] = 0x00;
-
- if (!access) {
- authChall[20] = (byte)(authChall[20] | 0x02);
- }
- if (userID) {
- authChall[20] = (byte)(authChall[20] | 0x01);
- }
-
- return authChall;
- }
-
- /**
- * Return the maximum allowed OBEX packet to transmit.
- * OBEX packets transmitted must be smaller than this value.
- * @param transport Reference to the ObexTransport in use.
- * @return the maximum allowed OBEX packet to transmit
- */
- public static int getMaxTxPacketSize(ObexTransport transport) {
- int size = transport.getMaxTransmitPacketSize();
- return validateMaxPacketSize(size);
- }
-
- /**
- * Return the maximum allowed OBEX packet to receive - used in OBEX connect.
- * @param transport
- * @return he maximum allowed OBEX packet to receive
- */
- public static int getMaxRxPacketSize(ObexTransport transport) {
- int size = transport.getMaxReceivePacketSize();
- return validateMaxPacketSize(size);
- }
-
- private static int validateMaxPacketSize(int size) {
- if (VDBG && (size > MAX_PACKET_SIZE_INT)) {
- Log.w(TAG, "The packet size supported for the connection (" + size + ") is larger"
- + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
- }
- if (size != -1 && size < MAX_PACKET_SIZE_INT) {
- if (size < LOWER_LIMIT_MAX_PACKET_SIZE) {
- throw new IllegalArgumentException(size + " is less that the lower limit: "
- + LOWER_LIMIT_MAX_PACKET_SIZE);
- }
- return size;
- }
- return MAX_PACKET_SIZE_INT;
- }
-}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
deleted file mode 100644
index 542b9c8b2492..000000000000
--- a/obex/javax/obex/ObexSession.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-import android.util.Log;
-
-/**
- * The <code>ObexSession</code> interface characterizes the term
- * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
- * could be the server-side view of an OBEX connection, or the client-side view
- * of the same connection, which is established by server's accepting of a
- * client issued "CONNECT".
- * <P>
- * This interface serves as the common super class for
- * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
- * @hide
- */
-public class ObexSession {
-
- private static final String TAG = "ObexSession";
- private static final boolean V = ObexHelper.VDBG;
-
- protected Authenticator mAuthenticator;
-
- protected byte[] mChallengeDigest;
-
- /**
- * Called when the server received an authentication challenge header. This
- * will cause the authenticator to handle the authentication challenge.
- * @param header the header with the authentication challenge
- * @return <code>true</code> if the last request should be resent;
- * <code>false</code> if the last request should not be resent
- * @throws IOException
- */
- public boolean handleAuthChall(HeaderSet header) throws IOException {
- if (mAuthenticator == null) {
- return false;
- }
-
- /*
- * An authentication challenge is made up of one required and two
- * optional tag length value triplets. The tag 0x00 is required to be in
- * the authentication challenge and it represents the challenge digest
- * that was received. The tag 0x01 is the options tag. This tag tracks
- * if user ID is required and if full access will be granted. The tag
- * 0x02 is the realm, which provides a description of which user name
- * and password to use.
- */
- byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall);
- byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall);
- byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall);
-
- String realm = null;
- if (description != null) {
- byte[] realmString = new byte[description.length - 1];
- System.arraycopy(description, 1, realmString, 0, realmString.length);
-
- switch (description[0] & 0xFF) {
-
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII:
- // ASCII encoding
- // Fall through
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1:
- // ISO-8859-1 encoding
- try {
- realm = new String(realmString, "ISO8859_1");
- } catch (Exception e) {
- throw new IOException("Unsupported Encoding Scheme");
- }
- break;
-
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE:
- // UNICODE Encoding
- realm = ObexHelper.convertToUnicode(realmString, false);
- break;
-
- default:
- throw new IOException("Unsupported Encoding Scheme");
- }
- }
-
- boolean isUserIDRequired = false;
- boolean isFullAccess = true;
- if (option != null) {
- if ((option[0] & 0x01) != 0) {
- isUserIDRequired = true;
- }
-
- if ((option[0] & 0x02) != 0) {
- isFullAccess = false;
- }
- }
-
- PasswordAuthentication result = null;
- header.mAuthChall = null;
-
- try {
- result = mAuthenticator
- .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
- } catch (Exception e) {
- if (V) Log.d(TAG, "Exception occured - returning false", e);
- return false;
- }
-
- /*
- * If no password is provided then we not resent the request
- */
- if (result == null) {
- return false;
- }
-
- byte[] password = result.getPassword();
- if (password == null) {
- return false;
- }
-
- byte[] userName = result.getUserName();
-
- /*
- * Create the authentication response header. It includes 1 required and
- * 2 option tag length value triples. The required triple has a tag of
- * 0x00 and is the response digest. The first optional tag is 0x01 and
- * represents the user ID. If no user ID is provided, then no user ID
- * will be sent. The second optional tag is 0x02 and is the challenge
- * that was received. This will always be sent
- */
- if (userName != null) {
- header.mAuthResp = new byte[38 + userName.length];
- header.mAuthResp[36] = (byte)0x01;
- header.mAuthResp[37] = (byte)userName.length;
- System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length);
- } else {
- header.mAuthResp = new byte[36];
- }
-
- // Create the secret String
- byte[] digest = new byte[challenge.length + password.length + 1];
- System.arraycopy(challenge, 0, digest, 0, challenge.length);
- // Insert colon between challenge and password
- digest[challenge.length] = (byte)0x3A;
- System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
-
- // Add the Response Digest
- header.mAuthResp[0] = (byte)0x00;
- header.mAuthResp[1] = (byte)0x10;
-
- System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16);
-
- // Add the challenge
- header.mAuthResp[18] = (byte)0x02;
- header.mAuthResp[19] = (byte)0x10;
- System.arraycopy(challenge, 0, header.mAuthResp, 20, 16);
-
- return true;
- }
-
- /**
- * Called when the server received an authentication response header. This
- * will cause the authenticator to handle the authentication response.
- * @param authResp the authentication response
- * @return <code>true</code> if the response passed; <code>false</code> if
- * the response failed
- */
- public boolean handleAuthResp(byte[] authResp) {
- if (mAuthenticator == null) {
- return false;
- }
- // get the correct password from the application
- byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
- (byte)0x01, authResp));
- if (correctPassword == null) {
- return false;
- }
-
- byte[] temp = new byte[correctPassword.length + 16];
-
- System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
- System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
-
- byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
- byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
-
- // compare the MD5 hash array .
- for (int i = 0; i < 16; i++) {
- if (correctResponse[i] != actualResponse[i]) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
deleted file mode 100644
index 4cef0b33df4f..000000000000
--- a/obex/javax/obex/ObexTransport.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * The <code>ObexTransport</code> interface defines the underlying transport
- * connection which carries the OBEX protocol( such as TCP, RFCOMM device file
- * exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
- * platform, Irda). This interface provides an abstract layer to be used by the
- * <code>ObexConnection</code>. Each kind of medium shall have its own
- * implementation to wrap and follow the same interface.
- * <P>
- * See section 1.2.2 of IrDA Object Exchange Protocol specification.
- * <P>
- * Different kind of medium may have different construction - for example, the
- * RFCOMM device file medium may be constructed from a file descriptor or simply
- * a string while the TCP medium usually from a socket.
- * @hide
- */
-public interface ObexTransport {
-
- void create() throws IOException;
-
- void listen() throws IOException;
-
- void close() throws IOException;
-
- void connect() throws IOException;
-
- void disconnect() throws IOException;
-
- InputStream openInputStream() throws IOException;
-
- OutputStream openOutputStream() throws IOException;
-
- DataInputStream openDataInputStream() throws IOException;
-
- DataOutputStream openDataOutputStream() throws IOException;
-
- /**
- * Must return the maximum allowed OBEX packet that can be sent over
- * the transport. For L2CAP this will be the Max SDU reported by the
- * peer device.
- * The returned value will be used to set the outgoing OBEX packet
- * size. Therefore this value shall not change.
- * For RFCOMM or other transport types where the OBEX packets size
- * is unrelated to the transport packet size, return -1;
- * Exception can be made (like PBAP transport) with a smaller value
- * to avoid bad effect on other profiles using the RFCOMM;
- * @return the maximum allowed OBEX packet that can be send over
- * the transport. Or -1 in case of don't care.
- */
- int getMaxTransmitPacketSize();
-
- /**
- * Must return the maximum allowed OBEX packet that can be received over
- * the transport. For L2CAP this will be the Max SDU configured for the
- * L2CAP channel.
- * The returned value will be used to validate the incoming packet size
- * values.
- * For RFCOMM or other transport types where the OBEX packets size
- * is unrelated to the transport packet size, return -1;
- * @return the maximum allowed OBEX packet that can be send over
- * the transport. Or -1 in case of don't care.
- */
- int getMaxReceivePacketSize();
-
- /**
- * Shall return true if the transport in use supports SRM.
- * @return
- * <code>true</code> if SRM operation is supported, and is to be enabled.
- * <code>false</code> if SRM operations are not supported, or should not be used.
- */
- boolean isSrmSupported();
-
-
-}
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
deleted file mode 100644
index 5b4d5ace3669..000000000000
--- a/obex/javax/obex/Operation.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * The <code>Operation</code> interface provides ways to manipulate a single
- * OBEX PUT or GET operation. The implementation of this interface sends OBEX
- * packets as they are built. If during the operation the peer in the operation
- * ends the operation, an <code>IOException</code> is thrown on the next read
- * from the input stream, write to the output stream, or call to
- * <code>sendHeaders()</code>.
- * <P>
- * <STRONG>Definition of methods inherited from <code>ContentConnection</code>
- * </STRONG>
- * <P>
- * <code>getEncoding()</code> will always return <code>null</code>. <BR>
- * <code>getLength()</code> will return the length specified by the OBEX Length
- * header or -1 if the OBEX Length header was not included. <BR>
- * <code>getType()</code> will return the value specified in the OBEX Type
- * header or <code>null</code> if the OBEX Type header was not included.<BR>
- * <P>
- * <STRONG>How Headers are Handled</STRONG>
- * <P>
- * As headers are received, they may be retrieved through the
- * <code>getReceivedHeaders()</code> method. If new headers are set during the
- * operation, the new headers will be sent during the next packet exchange.
- * <P>
- * <STRONG>PUT example</STRONG>
- * <P>
- * <PRE>
- * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj) throws IOException {
- * // Include the length header
- * head.setHeader(head.LENGTH, new Long(obj.length));
- * // Initiate the PUT request
- * Operation op = conn.put(head);
- * // Open the output stream to put the object to it
- * DataOutputStream out = op.openDataOutputStream();
- * // Send the object to the server
- * out.write(obj);
- * // End the transaction
- * out.close();
- * op.close();
- * }
- * </PRE>
- * <P>
- * <STRONG>GET example</STRONG>
- * <P>
- * <PRE>
- * byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
- * // Send the initial GET request to the server
- * Operation op = conn.get(head);
- * // Retrieve the length of the object being sent back
- * int length = op.getLength();
- * // Create space for the object
- * byte[] obj = new byte[length];
- * // Get the object from the input stream
- * DataInputStream in = trans.openDataInputStream();
- * in.read(obj);
- * // End the transaction
- * in.close();
- * op.close();
- * return obj;
- * }
- * </PRE>
- *
- * <H3>Client PUT Operation Flow</H3> For PUT operations, a call to
- * <code>close()</code> the <code>OutputStream</code> returned from
- * <code>openOutputStream()</code> or <code>openDataOutputStream()</code> will
- * signal that the request is done. (In OBEX terms, the End-Of-Body header
- * should be sent and the final bit in the request will be set.) At this point,
- * the reply from the server may begin to be processed. A call to
- * <code>getResponseCode()</code> will do an implicit close on the
- * <code>OutputStream</code> and therefore signal that the request is done.
- * <H3>Client GET Operation Flow</H3> For GET operation, a call to
- * <code>openInputStream()</code> or <code>openDataInputStream()</code> will
- * signal that the request is done. (In OBEX terms, the final bit in the request
- * will be set.) A call to <code>getResponseCode()</code> will cause an implicit
- * close on the <code>InputStream</code>. No further data may be read at this
- * point.
- * @hide
- */
-public interface Operation {
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object. No headers are sent in the abort request. This will end the
- * operation since <code>close()</code> will be called by this method.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server calls this method
- */
- void abort() throws IOException;
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- HeaderSet getReceivedHeader() throws IOException;
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- * or <code>ClientSession.createHeaderSet()</code>
- * @throws NullPointerException if <code>headers</code> if <code>null</code>
- */
- void sendHeaders(HeaderSet headers) throws IOException;
-
- /**
- * Returns the response code received from the server. Response codes are
- * defined in the <code>ResponseCodes</code> class.
- * @see ResponseCodes
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this object was created by an OBEX server
- */
- int getResponseCode() throws IOException;
-
- String getEncoding();
-
- long getLength();
-
- int getHeaderLength();
-
- String getType();
-
- InputStream openInputStream() throws IOException;
-
- DataInputStream openDataInputStream() throws IOException;
-
- OutputStream openOutputStream() throws IOException;
-
- DataOutputStream openDataOutputStream() throws IOException;
-
- void close() throws IOException;
-
- int getMaxPacketSize();
-
- public void noBodyHeader();
-}
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
deleted file mode 100644
index 326b1ff56a74..000000000000
--- a/obex/javax/obex/PasswordAuthentication.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * This class holds user name and password combinations.
- * @hide
- */
-public final class PasswordAuthentication {
-
- private byte[] mUserName;
-
- private final byte[] mPassword;
-
- /**
- * Creates a new <code>PasswordAuthentication</code> with the user name and
- * password provided.
- * @param userName the user name to include; this may be <code>null</code>
- * @param password the password to include in the response
- * @throws NullPointerException if <code>password</code> is
- * <code>null</code>
- */
- public PasswordAuthentication(final byte[] userName, final byte[] password) {
- if (userName != null) {
- mUserName = new byte[userName.length];
- System.arraycopy(userName, 0, mUserName, 0, userName.length);
- }
-
- mPassword = new byte[password.length];
- System.arraycopy(password, 0, mPassword, 0, password.length);
- }
-
- /**
- * Retrieves the user name that was specified in the constructor. The user
- * name may be <code>null</code>.
- * @return the user name
- */
- public byte[] getUserName() {
- return mUserName;
- }
-
- /**
- * Retrieves the password.
- * @return the password
- */
- public byte[] getPassword() {
- return mPassword;
- }
-}
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
deleted file mode 100644
index 5daee72474fc..000000000000
--- a/obex/javax/obex/PrivateInputStream.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.InputStream;
-import java.io.IOException;
-
-/**
- * This object provides an input stream to the Operation objects used in this
- * package.
- * @hide
- */
-public final class PrivateInputStream extends InputStream {
-
- private BaseStream mParent;
-
- private byte[] mData;
-
- private int mIndex;
-
- private boolean mOpen;
-
- /**
- * Creates an input stream for the <code>Operation</code> to read from
- * @param p the connection this input stream is for
- */
- public PrivateInputStream(BaseStream p) {
- mParent = p;
- mData = new byte[0];
- mIndex = 0;
- mOpen = true;
- }
-
- /**
- * Returns the number of bytes that can be read (or skipped over) from this
- * input stream without blocking by the next caller of a method for this
- * input stream. The next caller might be the same thread or or another
- * thread.
- * @return the number of bytes that can be read from this input stream
- * without blocking
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized int available() throws IOException {
- ensureOpen();
- return mData.length - mIndex;
- }
-
- /**
- * Reads the next byte of data from the input stream. The value byte is
- * returned as an int in the range 0 to 255. If no byte is available because
- * the end of the stream has been reached, the value -1 is returned. This
- * method blocks until input data is available, the end of the stream is
- * detected, or an exception is thrown.
- * @return the byte read from the input stream or -1 if it reaches the end of
- * stream
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized int read() throws IOException {
- ensureOpen();
- while (mData.length == mIndex) {
- if (!mParent.continueOperation(true, true)) {
- return -1;
- }
- }
- return (mData[mIndex++] & 0xFF);
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public synchronized int read(byte[] b, int offset, int length) throws IOException {
-
- if (b == null) {
- throw new IOException("buffer is null");
- }
- if ((offset | length) < 0 || length > b.length - offset) {
- throw new ArrayIndexOutOfBoundsException("index outof bound");
- }
- ensureOpen();
-
- int currentDataLength = mData.length - mIndex;
- int remainReadLength = length;
- int offset1 = offset;
- int result = 0;
-
- while (currentDataLength <= remainReadLength) {
- System.arraycopy(mData, mIndex, b, offset1, currentDataLength);
- mIndex += currentDataLength;
- offset1 += currentDataLength;
- result += currentDataLength;
- remainReadLength -= currentDataLength;
-
- if (!mParent.continueOperation(true, true)) {
- return result == 0 ? -1 : result;
- }
- currentDataLength = mData.length - mIndex;
- }
- if (remainReadLength > 0) {
- System.arraycopy(mData, mIndex, b, offset1, remainReadLength);
- mIndex += remainReadLength;
- result += remainReadLength;
- }
- return result;
- }
-
- /**
- * Allows the <code>OperationImpl</code> thread to add body data to the
- * input stream.
- * @param body the data to add to the stream
- * @param start the start of the body to array to copy
- */
- public synchronized void writeBytes(byte[] body, int start) {
-
- int length = (body.length - start) + (mData.length - mIndex);
- byte[] temp = new byte[length];
-
- System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex);
- System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start);
-
- mData = temp;
- mIndex = 0;
- notifyAll();
- }
-
- /**
- * Verifies that this stream is open
- * @throws IOException if the stream is not open
- */
- private void ensureOpen() throws IOException {
- mParent.ensureOpen();
- if (!mOpen) {
- throw new IOException("Input stream is closed");
- }
- }
-
- /**
- * Closes the input stream. If the input stream is already closed, do
- * nothing.
- * @throws IOException this will never happen
- */
- @Override
- public void close() throws IOException {
- mOpen = false;
- mParent.streamClosed(true);
- }
-}
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
deleted file mode 100644
index 713f4ae2cff3..000000000000
--- a/obex/javax/obex/PrivateOutputStream.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-
-/**
- * This object provides an output stream to the Operation objects used in this
- * package.
- * @hide
- */
-public final class PrivateOutputStream extends OutputStream {
-
- private BaseStream mParent;
-
- private ByteArrayOutputStream mArray;
-
- private boolean mOpen;
-
- private int mMaxPacketSize;
-
- /**
- * Creates an empty <code>PrivateOutputStream</code> to write to.
- * @param p the connection that this stream runs over
- */
- public PrivateOutputStream(BaseStream p, int maxSize) {
- mParent = p;
- mArray = new ByteArrayOutputStream();
- mMaxPacketSize = maxSize;
- mOpen = true;
- }
-
- /**
- * Determines how many bytes have been written to the output stream.
- * @return the number of bytes written to the output stream
- */
- public int size() {
- return mArray.size();
- }
-
- /**
- * Writes the specified byte to this output stream. The general contract for
- * write is that one byte is written to the output stream. The byte to be
- * written is the eight low-order bits of the argument b. The 24 high-order
- * bits of b are ignored.
- * @param b the byte to write
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized void write(int b) throws IOException {
- ensureOpen();
- mParent.ensureNotDone();
- mArray.write(b);
- if (mArray.size() == mMaxPacketSize) {
- mParent.continueOperation(true, false);
- }
- }
-
- @Override
- public void write(byte[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- @Override
- public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
- int offset1 = offset;
- int remainLength = count;
-
- if (buffer == null) {
- throw new IOException("buffer is null");
- }
- if ((offset | count) < 0 || count > buffer.length - offset) {
- throw new IndexOutOfBoundsException("index outof bound");
- }
-
- ensureOpen();
- mParent.ensureNotDone();
- while ((mArray.size() + remainLength) >= mMaxPacketSize) {
- int bufferLeft = mMaxPacketSize - mArray.size();
- mArray.write(buffer, offset1, bufferLeft);
- offset1 += bufferLeft;
- remainLength -= bufferLeft;
- mParent.continueOperation(true, false);
- }
- if (remainLength > 0) {
- mArray.write(buffer, offset1, remainLength);
- }
- }
-
- /**
- * Reads the bytes that have been written to this stream.
- * @param size the size of the array to return
- * @return the byte array that is written
- */
- public synchronized byte[] readBytes(int size) {
- if (mArray.size() > 0) {
- byte[] temp = mArray.toByteArray();
- mArray.reset();
- byte[] result = new byte[size];
- System.arraycopy(temp, 0, result, 0, size);
- if (temp.length != size) {
- mArray.write(temp, size, temp.length - size);
- }
- return result;
- } else {
- return null;
- }
- }
-
- /**
- * Verifies that this stream is open
- * @throws IOException if the stream is not open
- */
- private void ensureOpen() throws IOException {
- mParent.ensureOpen();
- if (!mOpen) {
- throw new IOException("Output stream is closed");
- }
- }
-
- /**
- * Closes the output stream. If the input stream is already closed, do
- * nothing.
- * @throws IOException this will never happen
- */
- @Override
- public void close() throws IOException {
- mOpen = false;
- mParent.streamClosed(false);
- }
-
- /**
- * Determines if the connection is closed
- * @return <code>true</code> if the connection is closed; <code>false</code>
- * if the connection is open
- */
- public boolean isClosed() {
- return !mOpen;
- }
-}
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
deleted file mode 100644
index a2b9a37b4af1..000000000000
--- a/obex/javax/obex/ResponseCodes.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * The <code>ResponseCodes</code> class contains the list of valid response
- * codes a server may send to a client.
- * <P>
- * <STRONG>IMPORTANT NOTE</STRONG>
- * <P>
- * The values in this interface represent the values defined in the IrOBEX
- * specification, which is different with the HTTP specification.
- * <P>
- * <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
- * further description since they are not defined in HTTP. The server will send
- * an <code>OBEX_DATABASE_FULL</code> message when the client requests that
- * something be placed into a database but the database is full (cannot take
- * more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
- * client wishes to access a database, database table, or database record that
- * has been locked.
- * @hide
- */
-public final class ResponseCodes {
-
- /**
- * Defines the OBEX CONTINUE response code.
- * <P>
- * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
- */
- public static final int OBEX_HTTP_CONTINUE = 0x90;
-
- /**
- * Defines the OBEX SUCCESS response code.
- * <P>
- * The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
- */
- public static final int OBEX_HTTP_OK = 0xA0;
-
- /**
- * Defines the OBEX CREATED response code.
- * <P>
- * The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
- */
- public static final int OBEX_HTTP_CREATED = 0xA1;
-
- /**
- * Defines the OBEX ACCEPTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
- */
- public static final int OBEX_HTTP_ACCEPTED = 0xA2;
-
- /**
- * Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
- */
- public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
-
- /**
- * Defines the OBEX NO CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
- */
- public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
-
- /**
- * Defines the OBEX RESET CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
- */
- public static final int OBEX_HTTP_RESET = 0xA5;
-
- /**
- * Defines the OBEX PARTIAL CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
- */
- public static final int OBEX_HTTP_PARTIAL = 0xA6;
-
- /**
- * Defines the OBEX MULTIPLE_CHOICES response code.
- * <P>
- * The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
- */
- public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
-
- /**
- * Defines the OBEX MOVED PERMANENTLY response code.
- * <P>
- * The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
- */
- public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
-
- /**
- * Defines the OBEX MOVED TEMPORARILY response code.
- * <P>
- * The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
- */
- public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
-
- /**
- * Defines the OBEX SEE OTHER response code.
- * <P>
- * The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
- */
- public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
-
- /**
- * Defines the OBEX NOT MODIFIED response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
- */
- public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
-
- /**
- * Defines the OBEX USE PROXY response code.
- * <P>
- * The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
- */
- public static final int OBEX_HTTP_USE_PROXY = 0xB5;
-
- /**
- * Defines the OBEX BAD REQUEST response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
- */
- public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
-
- /**
- * Defines the OBEX UNAUTHORIZED response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
- */
- public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
-
- /**
- * Defines the OBEX PAYMENT REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
- */
- public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
-
- /**
- * Defines the OBEX FORBIDDEN response code.
- * <P>
- * The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
- */
- public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
-
- /**
- * Defines the OBEX NOT FOUND response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
- */
- public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
-
- /**
- * Defines the OBEX METHOD NOT ALLOWED response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
- */
- public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
-
- /**
- * Defines the OBEX NOT ACCEPTABLE response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
- */
- public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
-
- /**
- * Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
- */
- public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
-
- /**
- * Defines the OBEX REQUEST TIME OUT response code.
- * <P>
- * The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
- */
- public static final int OBEX_HTTP_TIMEOUT = 0xC8;
-
- /**
- * Defines the OBEX METHOD CONFLICT response code.
- * <P>
- * The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
- */
- public static final int OBEX_HTTP_CONFLICT = 0xC9;
-
- /**
- * Defines the OBEX METHOD GONE response code.
- * <P>
- * The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
- */
- public static final int OBEX_HTTP_GONE = 0xCA;
-
- /**
- * Defines the OBEX METHOD LENGTH REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
- */
- public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
-
- /**
- * Defines the OBEX PRECONDITION FAILED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
- */
- public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
-
- /**
- * Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
- * <P>
- * The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
- */
- public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
-
- /**
- * Defines the OBEX REQUESTED URL TOO LARGE response code.
- * <P>
- * The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
- */
- public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
-
- /**
- * Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
- */
- public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
-
- /**
- * Defines the OBEX INTERNAL SERVER ERROR response code.
- * <P>
- * The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
- */
- public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
-
- /**
- * Defines the OBEX NOT IMPLEMENTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
- */
- public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
-
- /**
- * Defines the OBEX BAD GATEWAY response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
- */
- public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
-
- /**
- * Defines the OBEX SERVICE UNAVAILABLE response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
- */
- public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
-
- /**
- * Defines the OBEX GATEWAY TIMEOUT response code.
- * <P>
- * The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
- */
- public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
-
- /**
- * Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
- */
- public static final int OBEX_HTTP_VERSION = 0xD5;
-
- /**
- * Defines the OBEX DATABASE FULL response code.
- * <P>
- * The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
- */
- public static final int OBEX_DATABASE_FULL = 0xE0;
-
- /**
- * Defines the OBEX DATABASE LOCKED response code.
- * <P>
- * The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
- */
- public static final int OBEX_DATABASE_LOCKED = 0xE1;
-
- /**
- * Constructor does nothing.
- */
- private ResponseCodes() {
- }
-}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
deleted file mode 100644
index 15ea36789e2c..000000000000
--- a/obex/javax/obex/ServerOperation.java
+++ /dev/null
@@ -1,861 +0,0 @@
-/* Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.DataInputStream;
-import java.io.OutputStream;
-import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-
-import android.util.Log;
-
-/**
- * This class implements the Operation interface for server side connections.
- * <P>
- * <STRONG>Request Codes</STRONG> There are four different request codes that
- * are in this class. 0x02 is a PUT request that signals that the request is not
- * complete and requires an additional OBEX packet. 0x82 is a PUT request that
- * says that request is complete. In this case, the server can begin sending the
- * response. The 0x03 is a GET request that signals that the request is not
- * finished. When the server receives a 0x83, the client is signaling the server
- * that it is done with its request. TODO: Extend the ClientOperation and reuse
- * the methods defined TODO: in that class.
- * @hide
- */
-public final class ServerOperation implements Operation, BaseStream {
-
- private static final String TAG = "ServerOperation";
-
- private static final boolean V = ObexHelper.VDBG; // Verbose debugging
-
- public boolean isAborted;
-
- public HeaderSet requestHeader;
-
- public HeaderSet replyHeader;
-
- public boolean finalBitSet;
-
- private InputStream mInput;
-
- private ServerSession mParent;
-
- private int mMaxPacketLength;
-
- private int mResponseSize;
-
- private boolean mClosed;
-
- private boolean mGetOperation;
-
- private PrivateInputStream mPrivateInput;
-
- private PrivateOutputStream mPrivateOutput;
-
- private ObexTransport mTransport;
-
- private boolean mPrivateOutputOpen;
-
- private String mExceptionString;
-
- private ServerRequestHandler mListener;
-
- private boolean mRequestFinished;
-
- private boolean mHasBody;
-
- private boolean mSendBodyHeader = true;
- // Assume SRM disabled - needs to be explicit
- // enabled by client
- private boolean mSrmEnabled = false;
- // A latch - when triggered, there is not way back ;-)
- private boolean mSrmActive = false;
- // Set to true when a SRM enable response have been send
- private boolean mSrmResponseSent = false;
- // keep waiting until final-bit is received in request
- // to handle the case where the SRM enable header is in
- // a different OBEX packet than the SRMP header.
- private boolean mSrmWaitingForRemote = true;
- // Why should we wait? - currently not exposed to apps.
- private boolean mSrmLocalWait = false;
-
- /**
- * Creates new ServerOperation
- * @param p the parent that created this object
- * @param in the input stream to read from
- * @param out the output stream to write to
- * @param request the initial request that was received from the client
- * @param maxSize the max packet size that the client will accept
- * @param listen the listener that is responding to the request
- * @throws IOException if an IO error occurs
- */
- public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
- ServerRequestHandler listen) throws IOException {
-
- isAborted = false;
- mParent = p;
- mInput = in;
- mMaxPacketLength = maxSize;
- mClosed = false;
- requestHeader = new HeaderSet();
- replyHeader = new HeaderSet();
- mPrivateInput = new PrivateInputStream(this);
- mResponseSize = 3;
- mListener = listen;
- mRequestFinished = false;
- mPrivateOutputOpen = false;
- mHasBody = false;
- ObexPacket packet;
- mTransport = p.getTransport();
-
- /*
- * Determine if this is a PUT request
- */
- if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
- (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
- /*
- * It is a PUT request.
- */
- mGetOperation = false;
-
- /*
- * Determine if the final bit is set
- */
- if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
- finalBitSet = false;
- } else {
- finalBitSet = true;
- mRequestFinished = true;
- }
- } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
- (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
- /*
- * It is a GET request.
- */
- mGetOperation = true;
-
- // For Get request, final bit set is decided by server side logic
- finalBitSet = false;
-
- if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- mRequestFinished = true;
- }
- } else {
- throw new IOException("ServerOperation can not handle such request");
- }
-
- packet = ObexPacket.read(request, mInput);
-
- /*
- * Determine if the packet length is larger than this device can receive
- */
- if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
- throw new IOException("Packet received was too large. Length: "
- + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
- }
-
- /*
- * Determine if any headers were sent in the initial request
- */
- if (packet.mLength > 3) {
- if(!handleObexPacket(packet)) {
- return;
- }
- /* Don't Pre-Send continue when Remote requested for SRM
- * Let the Application confirm.
- */
- if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
- + " not hasBody case: " + mHasBody);
- if (!mHasBody && !mSrmEnabled) {
- while ((!mGetOperation) && (!finalBitSet)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- if (mPrivateInput.available() > 0) {
- break;
- }
- }
- }
- }
- /* Don't Pre-Send continue when Remote requested for SRM
- * Let the Application confirm.
- */
- if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
- + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
- while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
- && (mPrivateInput.available() == 0)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- if (mPrivateInput.available() > 0) {
- break;
- }
- }
-
- // wait for get request finished !!!!
- while (mGetOperation && !mRequestFinished) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- }
- }
-
- /**
- * Parse headers and update member variables
- * @param packet the received obex packet
- * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
- * response have been send. Else true.
- * @throws IOException
- */
- private boolean handleObexPacket(ObexPacket packet) throws IOException {
- byte[] body = updateRequestHeaders(packet);
-
- if (body != null) {
- mHasBody = true;
- }
- if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper
- .convertToLong(requestHeader.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (requestHeader.mAuthResp != null) {
- if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
- mExceptionString = "Authentication Failed";
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
- mClosed = true;
- requestHeader.mAuthResp = null;
- return false;
- }
- requestHeader.mAuthResp = null;
- }
-
- if (requestHeader.mAuthChall != null) {
- mParent.handleAuthChall(requestHeader);
- // send the auhtResp to the client
- replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
- System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
- replyHeader.mAuthResp.length);
- requestHeader.mAuthResp = null;
- requestHeader.mAuthChall = null;
- }
-
- if (body != null) {
- mPrivateInput.writeBytes(body, 1);
- }
- return true;
- }
-
- /**
- * Update the request header set, and sniff on SRM headers to update local state.
- * @param data the OBEX packet data
- * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
- * @throws IOException
- */
- private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
- byte[] body = null;
- if (packet.mPayload != null) {
- body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
- }
- Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if(mTransport.isSrmSupported() && srmMode != null
- && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
- mSrmEnabled = true;
- if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
- }
- checkForSrmWait(packet.mHeaderId);
- if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
- if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
- mSrmActive = true;
- }
- return body;
- }
-
- /**
- * Call this only when a complete request have been received.
- * (This is not optimal, but the current design is not really suited to
- * the way SRM is specified.)
- */
- private void checkForSrmWait(int headerId){
- if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
- || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
- || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
- try {
- mSrmWaitingForRemote = false;
- Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
- mSrmWaitingForRemote = true;
- // Clear the wait header, as the absents of the header when the final bit is set
- // indicates don't wait.
- requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
- }
- }
-
- public boolean isValidBody() {
- return mHasBody;
- }
-
- /**
- * Determines if the operation should continue or should wait. If it should
- * continue, this method will continue the operation.
- * @param sendEmpty if <code>true</code> then this will continue the
- * operation even if no headers will be sent; if <code>false</code>
- * then this method will only continue the operation if there are
- * headers to send
- * @param inStream if<code>true</code> the stream is input stream, otherwise
- * output stream
- * @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
- */
- public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
- throws IOException {
- if (!mGetOperation) {
- if (!finalBitSet) {
- if (sendEmpty) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- } else {
- if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- } else {
- return false;
- }
- }
- } else {
- return false;
- }
- } else {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- }
- }
-
- /**
- * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
- * will wait for a response from the client before ending unless SRM is active.
- * @param type the response code to send back to the client
- * @return <code>true</code> if the final bit was not set on the reply;
- * <code>false</code> if no reply was received because the operation
- * ended, an abort was received, the final bit was set in the
- * reply or SRM is active.
- * @throws IOException if an IO error occurs
- */
- public synchronized boolean sendReply(int type) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- boolean skipSend = false;
- boolean skipReceive = false;
- boolean srmRespSendPending = false;
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- replyHeader.mConnectionID = null;
- } else {
- replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- if(mSrmEnabled && !mSrmResponseSent) {
- // As we are not ensured that the SRM enable is in the first OBEX packet
- // We must check for each reply.
- if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
- replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
- srmRespSendPending = true;
- }
-
- if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
- replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
- }
-
- byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
- int bodyLength = -1;
- int orginalBodyLength = -1;
-
- if (mPrivateOutput != null) {
- bodyLength = mPrivateOutput.size();
- orginalBodyLength = bodyLength;
- }
-
- if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
-
- int end = 0;
- int start = 0;
-
- while (end != headerArray.length) {
- end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
- - ObexHelper.BASE_PACKET_LENGTH);
- if (end == -1) {
-
- mClosed = true;
-
- if (mPrivateInput != null) {
- mPrivateInput.close();
- }
-
- if (mPrivateOutput != null) {
- mPrivateOutput.close();
- }
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- throw new IOException("OBEX Packet exceeds max packet size");
- }
- byte[] sendHeader = new byte[end - start];
- System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
-
- mParent.sendResponse(type, sendHeader);
- start = end;
- }
-
- if (bodyLength > 0) {
- return true;
- } else {
- return false;
- }
-
- } else {
- out.write(headerArray);
- }
-
- // For Get operation: if response code is OBEX_HTTP_OK, then this is the
- // last packet; so set finalBitSet to true.
- if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
- finalBitSet = true;
- }
-
- if(mSrmActive) {
- if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
- mSrmResponseSent == true) {
- // we are in the middle of a SRM PUT operation, don't send a continue.
- skipSend = true;
- } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
- // We are still receiving the get request, receive, but don't send continue.
- skipSend = true;
- } else if(mGetOperation && mRequestFinished == true) {
- // All done receiving the GET request, send data to the client, without
- // expecting a continue.
- skipReceive = true;
- }
- if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
- + " skipReceive==" + skipReceive);
- }
- if(srmRespSendPending) {
- if(V)Log.v(TAG,
- "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
- mSrmResponseSent = true;
- }
-
- if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
- if (bodyLength > 0) {
- /*
- * Determine if I can send the whole body or just part of
- * the body. Remember that there is the 3 bytes for the
- * response message and 3 bytes for the header ID and length
- */
- if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
- bodyLength = mMaxPacketLength - headerArray.length - 6;
- }
-
- byte[] body = mPrivateOutput.readBytes(bodyLength);
-
- /*
- * Since this is a put request if the final bit is set or
- * the output stream is closed we need to send the 0x49
- * (End of Body) otherwise, we need to send 0x48 (Body)
- */
- if ((finalBitSet) || (mPrivateOutput.isClosed())) {
- if(mSendBodyHeader == true) {
- out.write(0x49);
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- out.write(body);
- }
- } else {
- if(mSendBodyHeader == true) {
- out.write(0x48);
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- out.write(body);
- }
- }
-
- }
- }
-
- if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
- if(mSendBodyHeader) {
- out.write(0x49);
- orginalBodyLength = 3;
- out.write((byte)(orginalBodyLength >> 8));
- out.write((byte)orginalBodyLength);
- }
- }
-
- if(skipSend == false) {
- mResponseSize = 3;
- mParent.sendResponse(type, out.toByteArray());
- }
-
- if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
-
- if(mGetOperation && skipReceive) {
- // Here we need to check for and handle abort (throw an exception).
- // Any other signal received should be discarded silently (only on server side)
- checkSrmRemoteAbort();
- } else {
- // Receive and handle data (only send reply if !skipSend)
- // Read a complete OBEX Packet
- ObexPacket packet = ObexPacket.read(mInput);
-
- int headerId = packet.mHeaderId;
- if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
- && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
- && (headerId != ObexHelper.OBEX_OPCODE_GET)
- && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
-
- /*
- * Determine if an ABORT was sent as the reply
- */
- if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
- handleRemoteAbort();
- } else {
- // TODO:shall we send this if it occurs during SRM? Errata on the subject
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
- mClosed = true;
- mExceptionString = "Bad Request Received";
- throw new IOException("Bad Request Received");
- }
- } else {
-
- if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
- finalBitSet = true;
- } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- mRequestFinished = true;
- }
-
- /*
- * Determine if the packet length is larger than the negotiated packet size
- */
- if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
- throw new IOException("Packet received was too large");
- }
-
- /*
- * Determine if any headers were sent in the initial request
- */
- if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
- if(handleObexPacket(packet) == false) {
- return false;
- }
- }
- }
-
- }
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * This method will look for an abort from the peer during a SRM transfer.
- * The function will not block if no data has been received from the remote device.
- * If data have been received, the function will block while reading the incoming
- * OBEX package.
- * An Abort request will be handled, and cause an IOException("Abort Received").
- * Other messages will be discarded silently as per GOEP specification.
- * @throws IOException if an abort request have been received.
- * TODO: I think this is an error in the specification. If we discard other messages,
- * the peer device will most likely stall, as it will not receive the expected
- * response for the message...
- * I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
- * header values shall be ignored by the receiving device."
- * If any signal is received during an active SRM transfer it is unexpected regardless
- * whether or not it contains SRM/SRMP headers...
- */
- private void checkSrmRemoteAbort() throws IOException {
- if(mInput.available() > 0) {
- ObexPacket packet = ObexPacket.read(mInput);
- /*
- * Determine if an ABORT was sent as the reply
- */
- if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
- handleRemoteAbort();
- } else {
- // TODO: should we throw an exception here anyway? - don't see how to
- // ignore SRM/SRMP headers without ignoring the complete signal
- // (in this particular case).
- Log.w(TAG, "Received unexpected request from client - discarding...\n"
- + " headerId: " + packet.mHeaderId + " length: " + packet.mLength);
- }
- }
- }
-
- private void handleRemoteAbort() throws IOException {
- /* TODO: To increase the speed of the abort operation in SRM, we need
- * to be able to flush the L2CAP queue for the PSM in use.
- * This could be implemented by introducing a control
- * message to be send over the socket, that in the abort case
- * could carry a flush command. */
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
- mClosed = true;
- isAborted = true;
- mExceptionString = "Abort Received";
- throw new IOException("Abort Received");
- }
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server called this method
- */
- public void abort() throws IOException {
- throw new IOException("Called from a server");
- }
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- public HeaderSet getReceivedHeader() throws IOException {
- ensureOpen();
- return requestHeader;
- }
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- */
- public void sendHeaders(HeaderSet headers) throws IOException {
- ensureOpen();
-
- if (headers == null) {
- throw new IOException("Headers may not be null");
- }
-
- int[] headerList = headers.getHeaderList();
- if (headerList != null) {
- for (int i = 0; i < headerList.length; i++) {
- replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
- }
-
- }
- }
-
- /**
- * Retrieves the response code retrieved from the server. Response codes are
- * defined in the <code>ResponseCodes</code> interface.
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet</code> in a <code>ClientSession</code>
- * object; if this is called from a server
- */
- public int getResponseCode() throws IOException {
- throw new IOException("Called from a server");
- }
-
- /**
- * Always returns <code>null</code>
- * @return <code>null</code>
- */
- public String getEncoding() {
- return null;
- }
-
- /**
- * Returns the type of content that the resource connected to is providing.
- * E.g. if the connection is via HTTP, then the value of the content-type
- * header field is returned.
- * @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
- */
- public String getType() {
- try {
- return (String)requestHeader.getHeader(HeaderSet.TYPE);
- } catch (IOException e) {
- return null;
- }
- }
-
- /**
- * Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header field
- * is returned.
- * @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
- */
- public long getLength() {
- try {
- Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
-
- if (temp == null) {
- return -1;
- } else {
- return temp.longValue();
- }
- } catch (IOException e) {
- return -1;
- }
- }
-
- public int getMaxPacketSize() {
- return mMaxPacketLength - 6 - getHeaderLength();
- }
-
- public int getHeaderLength() {
- long id = mListener.getConnectionId();
- if (id == -1) {
- replyHeader.mConnectionID = null;
- } else {
- replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
-
- return headerArray.length;
- }
-
- /**
- * Open and return an input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public InputStream openInputStream() throws IOException {
- ensureOpen();
- return mPrivateInput;
- }
-
- /**
- * Open and return a data input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public DataInputStream openDataInputStream() throws IOException {
- return new DataInputStream(openInputStream());
- }
-
- /**
- * Open and return an output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public OutputStream openOutputStream() throws IOException {
- ensureOpen();
-
- if (mPrivateOutputOpen) {
- throw new IOException("no more input streams available, stream already opened");
- }
-
- if (!mRequestFinished) {
- throw new IOException("no output streams available ,request not finished");
- }
-
- if (mPrivateOutput == null) {
- mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
- }
- mPrivateOutputOpen = true;
- return mPrivateOutput;
- }
-
- /**
- * Open and return a data output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public DataOutputStream openDataOutputStream() throws IOException {
- return new DataOutputStream(openOutputStream());
- }
-
- /**
- * Closes the connection and ends the transaction
- * @throws IOException if the operation has already ended or is closed
- */
- public void close() throws IOException {
- ensureOpen();
- mClosed = true;
- }
-
- /**
- * Verifies that the connection is open and no exceptions should be thrown.
- * @throws IOException if an exception needs to be thrown
- */
- public void ensureOpen() throws IOException {
- if (mExceptionString != null) {
- throw new IOException(mExceptionString);
- }
- if (mClosed) {
- throw new IOException("Operation has already ended");
- }
- }
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * <P>
- * Included to implement the BaseStream interface only. It does not do
- * anything on the server side since the operation of the Operation object
- * is not done until after the handler returns from its method.
- * @throws IOException if the operation is completed
- */
- public void ensureNotDone() throws IOException {
- }
-
- /**
- * Called when the output or input stream is closed. It does not do anything
- * on the server side since the operation of the Operation object is not
- * done until after the handler returns from its method.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- public void streamClosed(boolean inStream) throws IOException {
-
- }
-
- public void noBodyHeader(){
- mSendBodyHeader = false;
- }
-}
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
deleted file mode 100644
index 09cbc2ca489f..000000000000
--- a/obex/javax/obex/ServerRequestHandler.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * The <code>ServerRequestHandler</code> class defines an event listener that
- * will respond to OBEX requests made to the server.
- * <P>
- * The <code>onConnect()</code>, <code>onSetPath()</code>,
- * <code>onDelete()</code>, <code>onGet()</code>, and <code>onPut()</code>
- * methods may return any response code defined in the
- * <code>ResponseCodes</code> class except for <code>OBEX_HTTP_CONTINUE</code>.
- * If <code>OBEX_HTTP_CONTINUE</code> or a value not defined in the
- * <code>ResponseCodes</code> class is returned, the server implementation will
- * send an <code>OBEX_HTTP_INTERNAL_ERROR</code> response to the client.
- * <P>
- * <STRONG>Connection ID and Target Headers</STRONG>
- * <P>
- * According to the IrOBEX specification, a packet may not contain a Connection
- * ID and Target header. Since the Connection ID header is managed by the
- * implementation, it will not send a Connection ID header, if a Connection ID
- * was specified, in a packet that has a Target header. In other words, if an
- * application adds a Target header to a <code>HeaderSet</code> object used in
- * an OBEX operation and a Connection ID was specified, no Connection ID will be
- * sent in the packet containing the Target header.
- * <P>
- * <STRONG>CREATE-EMPTY Requests</STRONG>
- * <P>
- * A CREATE-EMPTY request allows clients to create empty objects on the server.
- * When a CREATE-EMPTY request is received, the <code>onPut()</code> method will
- * be called by the implementation. To differentiate between a normal PUT
- * request and a CREATE-EMPTY request, an application must open the
- * <code>InputStream</code> from the <code>Operation</code> object passed to the
- * <code>onPut()</code> method. For a PUT request, the application will be able
- * to read Body data from this <code>InputStream</code>. For a CREATE-EMPTY
- * request, there will be no Body data to read. Therefore, a call to
- * <code>InputStream.read()</code> will return -1.
- * @hide
- */
-public class ServerRequestHandler {
-
- private long mConnectionId;
-
- /**
- * Creates a <code>ServerRequestHandler</code>.
- */
- protected ServerRequestHandler() {
- /*
- * A connection ID of -1 implies there is no conenction ID
- */
- mConnectionId = -1;
- }
-
- /**
- * Sets the connection ID header to include in the reply packets.
- * @param connectionId the connection ID to use; -1 if no connection ID
- * should be sent
- * @throws IllegalArgumentException if <code>id</code> is not in the range
- * -1 to 2<sup>32</sup>-1
- */
- public void setConnectionId(final long connectionId) {
- if ((connectionId < -1) || (connectionId > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Illegal Connection ID");
- }
- mConnectionId = connectionId;
- }
-
- /**
- * Retrieves the connection ID that is being used in the present connection.
- * This method will return -1 if no connection ID is being used.
- * @return the connection id being used or -1 if no connection ID is being
- * used
- */
- public long getConnectionId() {
- return mConnectionId;
- }
-
- /**
- * Called when a CONNECT request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onConnect()</code> will always return an <code>OBEX_HTTP_OK</code>
- * response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onConnect(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_OK;
- }
-
- /**
- * Called when a DISCONNECT request is received.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- */
- public void onDisconnect(HeaderSet request, HeaderSet reply) {
- }
-
- /**
- * Called when a SETPATH request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onSetPath()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @param backup <code>true</code> if the client requests that the server
- * back up one directory before changing to the path described by
- * <code>name</code>; <code>false</code> to apply the request to the
- * present path
- * @param create <code>true</code> if the path should be created if it does
- * not already exist; <code>false</code> if the path should not be
- * created if it does not exist and an error code should be returned
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
-
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a DELETE request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onDelete()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onDelete(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a ABORT request is received.
- */
- public int onAbort(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a PUT request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onPut()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * If an ABORT request is received during the processing of a PUT request,
- * <code>op</code> will be closed by the implementation.
- * @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onPut(Operation operation) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a GET request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onGet()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * If an ABORT request is received during the processing of a GET request,
- * <code>op</code> will be closed by the implementation.
- * @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onGet(Operation operation) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when this object attempts to authenticate a client and the
- * authentication request fails because the response digest in the
- * authentication response header was wrong.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- * @param userName the user name returned in the authentication response;
- * <code>null</code> if no user name was provided in the response
- */
- public void onAuthenticationFailure(byte[] userName) {
- }
-
- /**
- * Called by ServerSession to update the status of current transaction
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- */
- public void updateStatus(String message) {
- }
-
- /**
- * Called when session is closed.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- */
- public void onClose() {
- }
-
- /**
- * Override to add Single Response Mode support - e.g. if the supplied
- * transport is l2cap.
- * @return True if SRM is supported, else False
- */
- public boolean isSrmSupported() {
- return false;
- }
-}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
deleted file mode 100644
index dbfeefdfb037..000000000000
--- a/obex/javax/obex/ServerSession.java
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Copyright (c) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import android.util.Log;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * This class in an implementation of the OBEX ServerSession.
- * @hide
- */
-public final class ServerSession extends ObexSession implements Runnable {
-
- private static final String TAG = "Obex ServerSession";
- private static final boolean V = ObexHelper.VDBG;
-
- private ObexTransport mTransport;
-
- private InputStream mInput;
-
- private OutputStream mOutput;
-
- private ServerRequestHandler mListener;
-
- private Thread mProcessThread;
-
- private int mMaxPacketLength;
-
- private boolean mClosed;
-
- /**
- * Creates new ServerSession.
- * @param trans the connection to the client
- * @param handler the event listener that will process requests
- * @param auth the authenticator to use with this connection
- * @throws IOException if an error occurred while opening the input and
- * output streams
- */
- public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
- throws IOException {
- mAuthenticator = auth;
- mTransport = trans;
- mInput = mTransport.openInputStream();
- mOutput = mTransport.openOutputStream();
- mListener = handler;
- mMaxPacketLength = 256;
-
- mClosed = false;
- mProcessThread = new Thread(this);
- mProcessThread.start();
- }
-
- /**
- * Processes requests made to the server and forwards them to the
- * appropriate event listener.
- */
- public void run() {
- try {
-
- boolean done = false;
- while (!done && !mClosed) {
- if(V) Log.v(TAG, "Waiting for incoming request...");
- int requestType = mInput.read();
- if(V) Log.v(TAG, "Read request: " + requestType);
- switch (requestType) {
- case ObexHelper.OBEX_OPCODE_CONNECT:
- handleConnectRequest();
- break;
-
- case ObexHelper.OBEX_OPCODE_DISCONNECT:
- handleDisconnectRequest();
- break;
-
- case ObexHelper.OBEX_OPCODE_GET:
- case ObexHelper.OBEX_OPCODE_GET_FINAL:
- handleGetRequest(requestType);
- break;
-
- case ObexHelper.OBEX_OPCODE_PUT:
- case ObexHelper.OBEX_OPCODE_PUT_FINAL:
- handlePutRequest(requestType);
- break;
-
- case ObexHelper.OBEX_OPCODE_SETPATH:
- handleSetPathRequest();
- break;
- case ObexHelper.OBEX_OPCODE_ABORT:
- handleAbortRequest();
- break;
-
- case -1:
- done = true;
- break;
-
- default:
-
- /*
- * Received a request type that is not recognized so I am
- * just going to read the packet and send a not implemented
- * to the client
- */
- int length = mInput.read();
- length = (length << 8) + mInput.read();
- for (int i = 3; i < length; i++) {
- mInput.read();
- }
- sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
- }
- }
-
- } catch (NullPointerException e) {
- Log.d(TAG, "Exception occured - ignoring", e);
- } catch (Exception e) {
- Log.d(TAG, "Exception occured - ignoring", e);
- }
- close();
- }
-
- /**
- * Handles a ABORT request from a client. This method will read the rest of
- * the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server.
- *
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleAbortRequest() throws IOException {
- int code = ResponseCodes.OBEX_HTTP_OK;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- int length = mInput.read();
- length = (length << 8) + mInput.read();
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- } else {
- for (int i = 3; i < length; i++) {
- mInput.read();
- }
- code = mListener.onAbort(request, reply);
- Log.v(TAG, "onAbort request handler return value- " + code);
- code = validateResponseCode(code);
- }
- sendResponse(code, null);
- }
-
- /**
- * Handles a PUT request from a client. This method will provide a
- * <code>ServerOperation</code> object to the request handler. The
- * <code>ServerOperation</code> object will handle the rest of the request.
- * It will also send replies and receive requests until the final reply
- * should be sent. When the final reply should be sent, this method will get
- * the response code to use and send the reply. The
- * <code>ServerOperation</code> object will always reply with a
- * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
- * needed.
- * @param type the type of request received; either 0x02 or 0x82
- * @throws IOException if an error occurred at the transport layer
- */
- private void handlePutRequest(int type) throws IOException {
- ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
- try {
- int response = -1;
-
- if ((op.finalBitSet) && !op.isValidBody()) {
- response = validateResponseCode(mListener
- .onDelete(op.requestHeader, op.replyHeader));
- } else {
- response = validateResponseCode(mListener.onPut(op));
- }
- if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
- op.sendReply(response);
- } else if (!op.isAborted) {
- // wait for the final bit
- while (!op.finalBitSet) {
- op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- }
- op.sendReply(response);
- }
- } catch (Exception e) {
- /*To fix bugs in aborted cases,
- *(client abort file transfer prior to the last packet which has the end of body header,
- *internal error should not be sent because server has already replied with
- *OK response in "sendReply")
- */
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- if (!op.isAborted) {
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- }
- }
- }
-
- /**
- * Handles a GET request from a client. This method will provide a
- * <code>ServerOperation</code> object to the request handler. The
- * <code>ServerOperation</code> object will handle the rest of the request.
- * It will also send replies and receive requests until the final reply
- * should be sent. When the final reply should be sent, this method will get
- * the response code to use and send the reply. The
- * <code>ServerOperation</code> object will always reply with a
- * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
- * needed.
- * @param type the type of request received; either 0x03 or 0x83
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleGetRequest(int type) throws IOException {
- ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
- try {
- int response = validateResponseCode(mListener.onGet(op));
-
- if (!op.isAborted) {
- op.sendReply(response);
- }
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- }
- }
-
- /**
- * Send standard response.
- * @param code the response code to send
- * @param header the headers to include in the response
- * @throws IOException if an IO error occurs
- */
- public void sendResponse(int code, byte[] header) throws IOException {
- int totalLength = 3;
- byte[] data = null;
- OutputStream op = mOutput;
- if (op == null) {
- return;
- }
-
- if (header != null) {
- totalLength += header.length;
- data = new byte[totalLength];
- data[0] = (byte)code;
- data[1] = (byte)(totalLength >> 8);
- data[2] = (byte)totalLength;
- System.arraycopy(header, 0, data, 3, header.length);
- } else {
- data = new byte[totalLength];
- data[0] = (byte)code;
- data[1] = (byte)0x00;
- data[2] = (byte)totalLength;
- }
- op.write(data);
- op.flush(); // TODO: Do we need to flush?
- }
-
- /**
- * Handles a SETPATH request from a client. This method will read the rest
- * of the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server
- * with the response code provided.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleSetPathRequest() throws IOException {
- int length;
- int flags;
- @SuppressWarnings("unused")
- int constants;
- int totalLength = 3;
- byte[] head = null;
- int code = -1;
- int bytesReceived;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- length = mInput.read();
- length = (length << 8) + mInput.read();
- flags = mInput.read();
- constants = mInput.read();
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 3;
- } else {
- if (length > 5) {
- byte[] headers = new byte[length - 5];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
- // the Auth chan is initiated by the server, client sent back the authResp .
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
- // the Auth challenge is initiated by the client
- // the server will send back the authResp to the client
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- reply.mAuthResp = new byte[request.mAuthResp.length];
- System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
- reply.mAuthResp.length);
- request.mAuthChall = null;
- request.mAuthResp = null;
- }
- boolean backup = false;
- boolean create = true;
- if (!((flags & 1) == 0)) {
- backup = true;
- }
- if (!((flags & 2) == 0)) {
- create = false;
- }
-
- try {
- code = mListener.onSetPath(request, reply, backup, create);
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- return;
- }
-
- code = validateResponseCode(code);
-
- if (reply.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
- } else {
- mChallengeDigest = null;
- }
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 3;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- }
- }
-
- // Compute Length of OBEX SETPATH packet
- byte[] replyData = new byte[totalLength];
- replyData[0] = (byte)code;
- replyData[1] = (byte)(totalLength >> 8);
- replyData[2] = (byte)totalLength;
- if (head != null) {
- System.arraycopy(head, 0, replyData, 3, head.length);
- }
- /*
- * Write the OBEX SETPATH packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3 to n: headers
- */
- mOutput.write(replyData);
- mOutput.flush();
- }
-
- /**
- * Handles a disconnect request from a client. This method will read the
- * rest of the request from the client. Assuming the request is valid, it
- * will create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleDisconnectRequest() throws IOException {
- int length;
- int code = ResponseCodes.OBEX_HTTP_OK;
- int totalLength = 3;
- byte[] head = null;
- int bytesReceived;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- length = mInput.read();
- length = (length << 8) + mInput.read();
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 3;
- } else {
- if (length > 3) {
- byte[] headers = new byte[length - 3];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
- }
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
-
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- request.mAuthChall = null;
- }
-
- try {
- mListener.onDisconnect(request, reply);
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- return;
- }
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 3;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- }
- }
-
- // Compute Length of OBEX CONNECT packet
- byte[] replyData;
- if (head != null) {
- replyData = new byte[3 + head.length];
- } else {
- replyData = new byte[3];
- }
- replyData[0] = (byte)code;
- replyData[1] = (byte)(totalLength >> 8);
- replyData[2] = (byte)totalLength;
- if (head != null) {
- System.arraycopy(head, 0, replyData, 3, head.length);
- }
- /*
- * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3 to n: headers
- */
- mOutput.write(replyData);
- mOutput.flush();
- }
-
- /**
- * Handles a connect request from a client. This method will read the rest
- * of the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server
- * with the response code provided.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleConnectRequest() throws IOException {
- int packetLength;
- @SuppressWarnings("unused")
- int version;
- @SuppressWarnings("unused")
- int flags;
- int totalLength = 7;
- byte[] head = null;
- int code = -1;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
- int bytesReceived;
-
- if(V) Log.v(TAG,"handleConnectRequest()");
-
- /*
- * Read in the length of the OBEX packet, OBEX version, flags, and max
- * packet length
- */
- packetLength = mInput.read();
- packetLength = (packetLength << 8) + mInput.read();
- if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
-
- version = mInput.read();
- flags = mInput.read();
- mMaxPacketLength = mInput.read();
- mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
-
- if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
- + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
-
- // should we check it?
- if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
- mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
- }
-
- if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
- Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
- + " is larger than the max size supported by the transport: "
- + ObexHelper.getMaxTxPacketSize(mTransport)
- + " Reducing to this size.");
- mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
- }
-
- if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 7;
- } else {
- if (packetLength > 7) {
- byte[] headers = new byte[packetLength - 7];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
- }
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- reply.mAuthResp = new byte[request.mAuthResp.length];
- System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
- reply.mAuthResp.length);
- request.mAuthChall = null;
- request.mAuthResp = null;
- }
-
- try {
- code = mListener.onConnect(request, reply);
- code = validateResponseCode(code);
-
- if (reply.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
- } else {
- mChallengeDigest = null;
- }
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 7;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- totalLength = 7;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
-
- }
- }
-
- // Compute Length of OBEX CONNECT packet
- byte[] length = ObexHelper.convertToByteArray(totalLength);
-
- /*
- * Write the OBEX CONNECT packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
- * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
- * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
- */
- byte[] sendData = new byte[totalLength];
- int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
- if (maxRxLength > mMaxPacketLength) {
- if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength +
- " and MaxNegotiated from Client: " + mMaxPacketLength);
- maxRxLength = mMaxPacketLength;
- }
- sendData[0] = (byte)code;
- sendData[1] = length[2];
- sendData[2] = length[3];
- sendData[3] = (byte)0x10;
- sendData[4] = (byte)0x00;
- sendData[5] = (byte)(maxRxLength >> 8);
- sendData[6] = (byte)(maxRxLength & 0xFF);
-
- if (head != null) {
- System.arraycopy(head, 0, sendData, 7, head.length);
- }
-
- mOutput.write(sendData);
- mOutput.flush();
- }
-
- /**
- * Closes the server session - in detail close I/O streams and the
- * underlying transport layer. Internal flag is also set so that later
- * attempt to read/write will throw an exception.
- */
- public synchronized void close() {
- if (mListener != null) {
- mListener.onClose();
- }
- try {
- /* Set state to closed before interrupting the thread by closing the streams */
- mClosed = true;
- if(mInput != null)
- mInput.close();
- if(mOutput != null)
- mOutput.close();
- if(mTransport != null)
- mTransport.close();
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
- }
- mTransport = null;
- mInput = null;
- mOutput = null;
- mListener = null;
- }
-
- /**
- * Verifies that the response code is valid. If it is not valid, it will
- * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
- * @param code the response code to check
- * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
- * if <code>code</code> is not valid
- */
- private int validateResponseCode(int code) {
-
- if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
- && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
- && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
- && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
- && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
- return code;
- }
- return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
-
- public ObexTransport getTransport() {
- return mTransport;
- }
-}
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
deleted file mode 100644
index 9836dd60f2c6..000000000000
--- a/obex/javax/obex/SessionNotifier.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-/**
- * The <code>SessionNotifier</code> interface defines a connection notifier for
- * server-side OBEX connections. When a <code>SessionNotifier</code> is created
- * and calls <code>acceptAndOpen()</code>, it will begin listening for clients
- * to create a connection at the transport layer. When the transport layer
- * connection is received, the <code>acceptAndOpen()</code> method will return a
- * <code>javax.microedition.io.Connection</code> that is the connection to the
- * client. The <code>acceptAndOpen()</code> method also takes a
- * <code>ServerRequestHandler</code> argument that will process the requests
- * from the client that connects to the server.
- * @hide
- */
-public interface SessionNotifier {
-
- /**
- * Waits for a transport layer connection to be established and specifies
- * the handler to handle the requests from the client. No authenticator is
- * associated with this connection, therefore, it is implementation
- * dependent as to how an authentication challenge and authentication
- * response header will be received and processed.
- * <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
- * on a <code>SessionNotifier</code> object that does not have a
- * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
- * for this object will be added to the SDDB. This method requests the BCC
- * to put the local device in connectable mode so that it will respond to
- * connection attempts by clients.
- * <P>
- * The following checks are done to verify that the service record provided
- * is valid. If any of these checks fail, then a
- * <code>ServiceRegistrationException</code> is thrown.
- * <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
- * attributes for a <code>btgoep</code> service record, must be present in
- * the <code>ServiceRecord</code> associated with this notifier.
- * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must not
- * have changed the RFCOMM server channel number
- * </UL>
- * <P>
- * This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely valid service record. It is the
- * responsibility of the application to ensure that the service record
- * follows all of the applicable syntactic and semantic rules for service
- * record correctness.
- * @param handler the request handler that will respond to OBEX requests
- * @return the connection to the client
- * @throws IOException if an error occurs in the transport layer
- * @throws NullPointerException if <code>handler</code> is <code>null</code>
- */
- ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
-
- /**
- * Waits for a transport layer connection to be established and specifies
- * the handler to handle the requests from the client and the
- * <code>Authenticator</code> to use to respond to authentication challenge
- * and authentication response headers.
- * <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
- * on a <code>SessionNotifier</code> object that does not have a
- * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
- * for this object will be added to the SDDB. This method requests the BCC
- * to put the local device in connectable mode so that it will respond to
- * connection attempts by clients.
- * <P>
- * The following checks are done to verify that the service record provided
- * is valid. If any of these checks fail, then a
- * <code>ServiceRegistrationException</code> is thrown.
- * <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
- * attributes for a <code>btgoep</code> service record, must be present in
- * the <code>ServiceRecord</code> associated with this notifier.
- * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must not
- * have changed the RFCOMM server channel number
- * </UL>
- * <P>
- * This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely valid service record. It is the
- * responsibility of the application to ensure that the service record
- * follows all of the applicable syntactic and semantic rules for service
- * record correctness.
- * @param handler the request handler that will respond to OBEX requests
- * @param auth the <code>Authenticator</code> to use with this connection;
- * if <code>null</code> then no <code>Authenticator</code> will be
- * used
- * @return the connection to the client
- * @throws IOException if an error occurs in the transport layer
- * @throws NullPointerException if <code>handler</code> is <code>null</code>
- */
- ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth) throws IOException;
-}
diff --git a/packages/CompanionDeviceManager/res/color/selector.xml b/packages/CompanionDeviceManager/res/color/selector.xml
index fda827d7f9c8..56e5dca0f72f 100644
--- a/packages/CompanionDeviceManager/res/color/selector.xml
+++ b/packages/CompanionDeviceManager/res/color/selector.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@android:color/darker_gray"/> <!-- pressed -->
- <item android:color="@android:color/white"/>
+ <item android:color="?android:attr/colorBackground"/>
</selector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
index ef7052d56a44..a017f4167907 100644
--- a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
+++ b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
@@ -16,7 +16,7 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape android:shape="rectangle">
- <corners android:radius="@*android:dimen/config_dialogCornerRadius" />
+ <corners android:radius="?android:attr/dialogCornerRadius" />
<solid android:color="?android:attr/colorBackground" />
</shape>
</inset>
diff --git a/packages/CompanionDeviceManager/res/values/themes.xml b/packages/CompanionDeviceManager/res/values/themes.xml
index 8559ef64f2c9..72404322c25e 100644
--- a/packages/CompanionDeviceManager/res/values/themes.xml
+++ b/packages/CompanionDeviceManager/res/values/themes.xml
@@ -17,9 +17,7 @@
<resources>
<style name="ChooserActivity"
- parent="@style/Theme.AppCompat.Light.Dialog">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog">
<item name="*android:windowFixedHeightMajor">100%</item>
<item name="*android:windowFixedHeightMinor">100%</item>
<item name="android:windowBackground">@android:color/transparent</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index a6a8fcf9af62..ae0c8ccdf006 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -49,7 +49,7 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
-import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -59,7 +59,7 @@ import java.util.List;
* A CompanionDevice activity response for showing the available
* nearby devices to be associated with.
*/
-public class CompanionDeviceActivity extends AppCompatActivity {
+public class CompanionDeviceActivity extends FragmentActivity {
private static final boolean DEBUG = false;
private static final String TAG = CompanionDeviceActivity.class.getSimpleName();
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index e879e40247cf..a6269711055a 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,13 +28,13 @@ import java.util.Objects;
@SystemApi
public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
- private final StaticIpConfiguration mIpConfig;
+ private final IpConfiguration mIpConfig;
@NonNull
private final NetworkCapabilities mNetworkCapabilities;
@NonNull
- public StaticIpConfiguration getIpConfig() {
- return new StaticIpConfiguration(mIpConfig);
+ public IpConfiguration getIpConfiguration() {
+ return new IpConfiguration(mIpConfig);
}
@NonNull
@@ -41,20 +42,75 @@ public final class EthernetNetworkUpdateRequest implements Parcelable {
return new NetworkCapabilities(mNetworkCapabilities);
}
- public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+ private EthernetNetworkUpdateRequest(@NonNull final IpConfiguration ipConfig,
@NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
Objects.requireNonNull(networkCapabilities);
- mIpConfig = new StaticIpConfiguration(ipConfig);
+ mIpConfig = new IpConfiguration(ipConfig);
mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
- mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
+ mIpConfig = IpConfiguration.CREATOR.createFromParcel(source);
mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
}
+ /**
+ * Builder used to create {@link EthernetNetworkUpdateRequest} objects.
+ */
+ public static final class Builder {
+ @Nullable
+ private IpConfiguration mBuilderIpConfig;
+ @Nullable
+ private NetworkCapabilities mBuilderNetworkCapabilities;
+
+ public Builder(){}
+
+ /**
+ * Constructor to populate the builder's values with an already built
+ * {@link EthernetNetworkUpdateRequest}.
+ * @param request the {@link EthernetNetworkUpdateRequest} to populate with.
+ */
+ public Builder(@NonNull final EthernetNetworkUpdateRequest request) {
+ Objects.requireNonNull(request);
+ mBuilderIpConfig = new IpConfiguration(request.mIpConfig);
+ mBuilderNetworkCapabilities = new NetworkCapabilities(request.mNetworkCapabilities);
+ }
+
+ /**
+ * Set the {@link IpConfiguration} to be used with the {@code Builder}.
+ * @param ipConfig the {@link IpConfiguration} to set.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setIpConfiguration(@NonNull final IpConfiguration ipConfig) {
+ Objects.requireNonNull(ipConfig);
+ mBuilderIpConfig = new IpConfiguration(ipConfig);
+ return this;
+ }
+
+ /**
+ * Set the {@link NetworkCapabilities} to be used with the {@code Builder}.
+ * @param nc the {@link NetworkCapabilities} to set.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setNetworkCapabilities(@NonNull final NetworkCapabilities nc) {
+ Objects.requireNonNull(nc);
+ mBuilderNetworkCapabilities = new NetworkCapabilities(nc);
+ return this;
+ }
+
+ /**
+ * Build {@link EthernetNetworkUpdateRequest} return the current update request.
+ */
+ @NonNull
+ public EthernetNetworkUpdateRequest build() {
+ return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities);
+ }
+ }
+
@Override
public String toString() {
return "EthernetNetworkUpdateRequest{"
@@ -68,7 +124,7 @@ public final class EthernetNetworkUpdateRequest implements Parcelable {
if (o == null || getClass() != o.getClass()) return false;
EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
- return Objects.equals(that.getIpConfig(), mIpConfig)
+ return Objects.equals(that.getIpConfiguration(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 7a5ea472b532..1b7298a332de 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -19,6 +19,7 @@ android_library {
"androidx.appcompat_appcompat",
"androidx.lifecycle_lifecycle-runtime",
"androidx.mediarouter_mediarouter-nodeps",
+ "com.google.android.material_material",
"iconloader",
"WifiTrackerLibRes",
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 77c4533b9f46..5e3907caf860 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -31,7 +31,7 @@
<!-- Dialog accent color -->
<color name="settingslib_dialog_accent">@android:color/system_accent1_100</color>
<!-- Dialog background color. -->
- <color name="settingslib_dialog_background">@android:color/system_neutral1_800</color>
+ <color name="settingslib_dialog_background">@color/settingslib_surface_dark</color>
<!-- Dialog error color. -->
<color name="settingslib_dialog_colorError">#f28b82</color> <!-- Red 300 -->
@@ -49,4 +49,8 @@
<color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
<color name="settingslib_ripple_color">@color/settingslib_material_grey_900</color>
+
+ <color name="settingslib_surface_dark">@android:color/system_neutral1_800</color>
+
+ <color name="settingslib_colorSurface">@color/settingslib_surface_dark</color>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 6adb7899fcc5..c4dbc5edbe79 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -71,5 +71,12 @@
<color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color>
<color name="settingslib_ripple_color">?android:attr/colorControlHighlight</color>
+
<color name="settingslib_material_grey_900">#ff212121</color>
+
+ <color name="settingslib_colorAccentPrimary">@color/settingslib_accent_primary_device_default</color>
+
+ <color name="settingslib_colorAccentSecondary">@color/settingslib_accent_secondary_device_default</color>
+
+ <color name="settingslib_colorSurface">@color/settingslib_surface_light</color>
</resources>
diff --git a/packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml
new file mode 100644
index 000000000000..9a093601a92c
--- /dev/null
+++ b/packages/SettingsLib/res/color-night-v31/settingslib_tabs_indicator_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/settingslib_colorAccentSecondary" />
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml b/packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml
new file mode 100644
index 000000000000..33f96df8d5b5
--- /dev/null
+++ b/packages/SettingsLib/res/color-night-v31/settingslib_tabs_text_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:color="?android:attr/textColorPrimaryInverse"/>
+ <item android:state_enabled="false" android:color="?android:attr/textColorTertiary"/>
+ <item android:color="?android:attr/textColorSecondary"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml
new file mode 100644
index 000000000000..57fef52f15bd
--- /dev/null
+++ b/packages/SettingsLib/res/color-v31/settingslib_tabs_indicator_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/settingslib_colorAccentPrimary" />
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml b/packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml
new file mode 100644
index 000000000000..df2346d7175e
--- /dev/null
+++ b/packages/SettingsLib/res/color-v31/settingslib_tabs_text_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:color="?android:attr/textColorPrimary"/>
+ <item android:state_enabled="false" android:color="?android:attr/textColorTertiary"/>
+ <item android:color="?android:attr/textColorSecondary"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml b/packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml
new file mode 100644
index 000000000000..f10b56315546
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-v31/settingslib_tabs_background.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <inset
+ android:insetLeft="4dp"
+ android:insetRight="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="12dp" />
+ <solid android:color="?android:colorControlHighlight" />
+ </shape>
+ </inset>
+ </item>
+
+ <item android:id="@android:id/background">
+ <inset
+ android:insetLeft="4dp"
+ android:insetRight="4dp">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_colorSurface" />
+ <corners android:radius="12dp" />
+ </shape>
+ </inset>
+ </item>
+</ripple>
diff --git a/packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml b/packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml
new file mode 100644
index 000000000000..f5a97825870c
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-v31/settingslib_tabs_indicator_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetRight="4dp">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_tabs_indicator_color" />
+ <corners android:radius="12dp" />
+ </shape>
+</inset> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-v31/styles.xml b/packages/SettingsLib/res/values-v31/styles.xml
new file mode 100644
index 000000000000..343de2cdf47a
--- /dev/null
+++ b/packages/SettingsLib/res/values-v31/styles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <style name="SettingsLibTabsTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="SettingsLibTabsStyle" parent="Base.Widget.Design.TabLayout">
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">48dp</item>
+ <item name="android:layout_marginStart">?android:attr/listPreferredItemPaddingStart</item>
+ <item name="android:layout_marginEnd">?android:attr/listPreferredItemPaddingEnd</item>
+ <item name="tabGravity">fill</item>
+ <item name="tabBackground">@drawable/settingslib_tabs_background</item>
+ <item name="tabIndicator">@drawable/settingslib_tabs_indicator_background</item>
+ <item name="tabIndicatorColor">@color/settingslib_tabs_indicator_color</item>
+ <item name="tabIndicatorFullWidth">true</item>
+ <item name="tabIndicatorGravity">stretch</item>
+ <item name="tabIndicatorAnimationMode">fade</item>
+ <item name="tabIndicatorAnimationDuration">0</item>
+ <item name="tabTextAppearance">@style/SettingsLibTabsTextAppearance</item>
+ <item name="tabTextColor">@color/settingslib_tabs_text_color</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 014a033a4c01..da0381b68278 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1311,8 +1311,8 @@
<!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_enable_dialog_turn_on">Turn on</string>
- <!-- Priority mode: Title for the Priority mode dialog to turn on Priority mode. [CHAR LIMIT=50]-->
- <string name="zen_mode_settings_turn_on_dialog_title" translatable="false">Turn on Priority mode</string>
+ <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
+ <string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
<!-- Sound: Summary for the Do not Disturb option when there is no automatic rules turned on. [CHAR LIMIT=NONE]-->
<string name="zen_mode_settings_summary_off">Never</string>
<!--[CHAR LIMIT=40] Zen Interruption level: Priority. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 61b8911acea4..0cb2c0b22a4c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -21,7 +21,8 @@ import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -30,15 +31,15 @@ import android.graphics.Paint;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.StrictMode;
-import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.util.EventLog;
import android.util.Log;
import androidx.core.content.FileProvider;
+import com.android.settingslib.utils.ThreadUtils;
+
import libcore.io.Streams;
import java.io.File;
@@ -47,39 +48,64 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.ExecutionException;
class AvatarPhotoController {
+
+ interface AvatarUi {
+ boolean isFinishing();
+
+ void returnUriResult(Uri uri);
+
+ void startActivityForResult(Intent intent, int resultCode);
+
+ boolean startSystemActivityForResult(Intent intent, int resultCode);
+
+ int getPhotoSize();
+ }
+
+ interface ContextInjector {
+ File getCacheDir();
+
+ Uri createTempImageUri(File parentDir, String fileName, boolean purge);
+
+ ContentResolver getContentResolver();
+ }
+
private static final String TAG = "AvatarPhotoController";
- private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
- private static final int REQUEST_CODE_TAKE_PHOTO = 1002;
- private static final int REQUEST_CODE_CROP_PHOTO = 1003;
- // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI
- // so we need a default photo size
- private static final int DEFAULT_PHOTO_SIZE = 500;
+ static final int REQUEST_CODE_CHOOSE_PHOTO = 1001;
+ static final int REQUEST_CODE_TAKE_PHOTO = 1002;
+ static final int REQUEST_CODE_CROP_PHOTO = 1003;
private static final String IMAGES_DIR = "multi_user";
+ private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
private final int mPhotoSize;
- private final AvatarPickerActivity mActivity;
- private final String mFileAuthority;
+ private final AvatarUi mAvatarUi;
+ private final ContextInjector mContextInjector;
private final File mImagesDir;
+ private final Uri mPreCropPictureUri;
private final Uri mCropPictureUri;
private final Uri mTakePictureUri;
- AvatarPhotoController(AvatarPickerActivity activity, boolean waiting, String fileAuthority) {
- mActivity = activity;
- mFileAuthority = fileAuthority;
+ AvatarPhotoController(AvatarUi avatarUi, ContextInjector contextInjector, boolean waiting) {
+ mAvatarUi = avatarUi;
+ mContextInjector = contextInjector;
- mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR);
+ mImagesDir = new File(mContextInjector.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting);
- mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting);
- mPhotoSize = getPhotoSize(activity);
+ mPreCropPictureUri = mContextInjector
+ .createTempImageUri(mImagesDir, PRE_CROP_PICTURE_FILE_NAME, !waiting);
+ mCropPictureUri =
+ mContextInjector.createTempImageUri(mImagesDir, CROP_PICTURE_FILE_NAME, !waiting);
+ mTakePictureUri =
+ mContextInjector.createTempImageUri(mImagesDir, TAKE_PICTURE_FILE_NAME, !waiting);
+ mPhotoSize = mAvatarUi.getPhotoSize();
}
/**
@@ -102,16 +128,12 @@ class AvatarPhotoController {
switch (requestCode) {
case REQUEST_CODE_CROP_PHOTO:
- mActivity.returnUriResult(pictureUri);
+ mAvatarUi.returnUriResult(pictureUri);
return true;
case REQUEST_CODE_TAKE_PHOTO:
case REQUEST_CODE_CHOOSE_PHOTO:
if (mTakePictureUri.equals(pictureUri)) {
- if (PhotoCapabilityUtils.canCropPhoto(mActivity)) {
- cropPhoto();
- } else {
- onPhotoNotCropped(pictureUri);
- }
+ cropPhoto(pictureUri);
} else {
copyAndCropPhoto(pictureUri);
}
@@ -123,55 +145,52 @@ class AvatarPhotoController {
void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
appendOutputExtra(intent, mTakePictureUri);
- mActivity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
+ mAvatarUi.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
}
void choosePhoto() {
Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES, null);
intent.setType("image/*");
- mActivity.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
+ mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
}
private void copyAndCropPhoto(final Uri pictureUri) {
- // TODO: Replace AsyncTask
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- final ContentResolver cr = mActivity.getContentResolver();
+ try {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final ContentResolver cr = mContextInjector.getContentResolver();
try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mTakePictureUri)) {
+ OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to copy photo", e);
+ return;
}
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (!mActivity.isFinishing() && !mActivity.isDestroyed()) {
- cropPhoto();
- }
- }
- }.execute();
+ ThreadUtils.postOnMainThread(() -> {
+ if (!mAvatarUi.isFinishing()) {
+ cropPhoto(mPreCropPictureUri);
+ }
+ });
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error performing copy-and-crop", e);
+ }
}
- private void cropPhoto() {
+ private void cropPhoto(final Uri pictureUri) {
// TODO: Use a public intent, when there is one.
Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(mTakePictureUri, "image/*");
+ intent.setDataAndType(pictureUri, "image/*");
appendOutputExtra(intent, mCropPictureUri);
appendCropExtras(intent);
- if (intent.resolveActivity(mActivity.getPackageManager()) != null) {
- try {
- StrictMode.disableDeathOnFileUriExposure();
- mActivity.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ if (mAvatarUi.startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
+ return;
}
- } else {
- onPhotoNotCropped(mTakePictureUri);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
}
+ onPhotoNotCropped(pictureUri);
}
private void appendOutputExtra(Intent intent, Uri pictureUri) {
@@ -192,24 +211,22 @@ class AvatarPhotoController {
}
private void onPhotoNotCropped(final Uri data) {
- // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
+ try {
+ ThreadUtils.postOnBackgroundThread(() -> {
// Scale and crop to a square aspect ratio
Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(croppedImage);
Bitmap fullImage;
try {
- InputStream imageStream = mActivity.getContentResolver()
+ InputStream imageStream = mContextInjector.getContentResolver()
.openInputStream(data);
fullImage = BitmapFactory.decodeStream(imageStream);
} catch (FileNotFoundException fe) {
- return null;
+ return;
}
if (fullImage != null) {
- int rotation = getRotation(mActivity, data);
+ int rotation = getRotation(data);
final int squareSize = Math.min(fullImage.getWidth(),
fullImage.getHeight());
final int left = (fullImage.getWidth() - squareSize) / 2;
@@ -222,29 +239,27 @@ class AvatarPhotoController {
matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
canvas.drawBitmap(fullImage, matrix, new Paint());
- return croppedImage;
- } else {
- // Bah! Got nothin.
- return null;
- }
- }
+ saveBitmapToFile(croppedImage, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- saveBitmapToFile(bitmap, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
- mActivity.returnUriResult(mCropPictureUri);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ ThreadUtils.postOnMainThread(() -> {
+ mAvatarUi.returnUriResult(mCropPictureUri);
+ });
+ }
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error performing internal crop", e);
+ }
}
/**
* Reads the image's exif data and determines the rotation degree needed to display the image
* in portrait mode.
*/
- private int getRotation(Context context, Uri selectedImage) {
+ private int getRotation(Uri selectedImage) {
int rotation = -1;
try {
- InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
+ InputStream imageStream =
+ mContextInjector.getContentResolver().openInputStream(selectedImage);
ExifInterface exif = new ExifInterface(imageStream);
rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
} catch (IOException exception) {
@@ -274,24 +289,74 @@ class AvatarPhotoController {
}
}
- private static int getPhotoSize(Context context) {
- try (Cursor cursor = context.getContentResolver().query(
- ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
- new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) {
- if (cursor != null) {
- cursor.moveToFirst();
- return cursor.getInt(0);
- } else {
- return DEFAULT_PHOTO_SIZE;
+ static class AvatarUiImpl implements AvatarUi {
+ private final AvatarPickerActivity mActivity;
+
+ AvatarUiImpl(AvatarPickerActivity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public boolean isFinishing() {
+ return mActivity.isFinishing() || mActivity.isDestroyed();
+ }
+
+ @Override
+ public void returnUriResult(Uri uri) {
+ mActivity.returnUriResult(uri);
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int resultCode) {
+ mActivity.startActivityForResult(intent, resultCode);
+ }
+
+ @Override
+ public boolean startSystemActivityForResult(Intent intent, int code) {
+ ActivityInfo info = intent.resolveActivityInfo(mActivity.getPackageManager(),
+ PackageManager.MATCH_SYSTEM_ONLY);
+ if (info == null) {
+ Log.w(TAG, "No system package activity could be found for code " + code);
+ return false;
}
+ intent.setPackage(info.packageName);
+ mActivity.startActivityForResult(intent, code);
+ return true;
+ }
+
+ @Override
+ public int getPhotoSize() {
+ return mActivity.getResources()
+ .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
}
}
- private Uri createTempImageUri(Context context, String fileName, boolean purge) {
- final File fullPath = new File(mImagesDir, fileName);
- if (purge) {
- fullPath.delete();
+ static class ContextInjectorImpl implements ContextInjector {
+ private final Context mContext;
+ private final String mFileAuthority;
+
+ ContextInjectorImpl(Context context, String fileAuthority) {
+ mContext = context;
+ mFileAuthority = fileAuthority;
+ }
+
+ @Override
+ public File getCacheDir() {
+ return mContext.getCacheDir();
+ }
+
+ @Override
+ public Uri createTempImageUri(File parentDir, String fileName, boolean purge) {
+ final File fullPath = new File(parentDir, fileName);
+ if (purge) {
+ fullPath.delete();
+ }
+ return FileProvider.getUriForFile(mContext, mFileAuthority, fullPath);
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContext.getContentResolver();
}
- return FileProvider.getUriForFile(context, mFileAuthority, fullPath);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 1e1dfae9f7ac..75bb70a123c3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -95,7 +95,9 @@ public class AvatarPickerActivity extends Activity {
restoreState(savedInstanceState);
mAvatarPhotoController = new AvatarPhotoController(
- this, mWaitingForActivityResult, getFileAuthority());
+ new AvatarPhotoController.AvatarUiImpl(this),
+ new AvatarPhotoController.ContextInjectorImpl(this, getFileAuthority()),
+ mWaitingForActivityResult);
}
private void setUpButtons() {
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index da808dd54141..2a4dfdd84c63 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -25,10 +25,19 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-
<application>
<uses-library android:name="android.test.runner" />
<activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="com.android.settingslib.test"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/file_paths" />
+ </provider>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/packages/SystemUI/res/values-w650dp-land/dimens.xml b/packages/SettingsLib/tests/integ/res/xml/file_paths.xml
index 97b6da1ac6a7..ccd11a46e429 100644
--- a/packages/SystemUI/res/values-w650dp-land/dimens.xml
+++ b/packages/SettingsLib/tests/integ/res/xml/file_paths.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,7 +13,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">-1px</dimen> <!-- match_parent -->
-</resources>
+
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Offer access to files under Context.getCacheDir() -->
+ <cache-path name="my_cache" />
+</paths>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
new file mode 100644
index 000000000000..9ebdba300266
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_CHOOSE_PHOTO;
+import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_CROP_PHOTO;
+import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_TAKE_PHOTO;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class AvatarPhotoControllerTest {
+
+ private static final long TIMEOUT_MILLIS = 5000;
+ private static final int PHOTO_SIZE = 200;
+
+ @Mock AvatarPhotoController.AvatarUi mMockAvatarUi;
+
+ private File mImagesDir;
+ private AvatarPhotoController mController;
+ private Uri mTakePhotoUri = Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/TakeEditUserPhoto.jpg");
+ private Uri mCropPhotoUri = Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/CropEditUserPhoto.jpg");
+ private Context mContext = InstrumentationRegistry.getTargetContext();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
+ when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
+
+ mImagesDir = new File(
+ InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
+ mImagesDir.mkdir();
+
+ AvatarPhotoController.ContextInjector contextInjector =
+ new AvatarPhotoController.ContextInjectorImpl(
+ InstrumentationRegistry.getTargetContext(), "com.android.settingslib.test");
+ mController = new AvatarPhotoController(mMockAvatarUi, contextInjector, false);
+ }
+
+ @After
+ public void tearDown() {
+ mImagesDir.delete();
+ }
+
+ @Test
+ public void takePhotoHasCorrectIntentAndResultCode() {
+ mController.takePhoto();
+
+ verifyStartActivityForResult(
+ MediaStore.ACTION_IMAGE_CAPTURE_SECURE, REQUEST_CODE_TAKE_PHOTO);
+ }
+
+ @Test
+ public void choosePhotoHasCorrectIntentAndResultCode() {
+ mController.choosePhoto();
+
+ verifyStartActivityForResult(
+ MediaStore.ACTION_PICK_IMAGES, REQUEST_CODE_CHOOSE_PHOTO);
+ }
+
+ @Test
+ public void takePhotoIsFollowedByCrop() throws IOException {
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
+
+ verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ }
+
+ @Test
+ public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
+ }
+
+ @Test
+ public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(mTakePhotoUri);
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
+
+ verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ }
+
+ @Test
+ public void takePhotoIsNotFollowedByCropIntentWhenCropNotSupported() throws IOException {
+ when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
+
+ File file = new File(mImagesDir, "file.txt");
+ saveBitmapToFile(file);
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
+ }
+
+ @Test
+ public void choosePhotoIsFollowedByCrop() throws IOException {
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
+
+ verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ }
+
+ @Test
+ public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
+ }
+
+ @Test
+ public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(mTakePhotoUri);
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
+
+ verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ }
+
+ @Test
+ public void cropPhotoResultIsReturnedIfResultOkAndContent() {
+ Intent intent = new Intent();
+ intent.setData(mCropPhotoUri);
+ mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
+ public void cropPhotoResultIsNotReturnedIfResultCancel() {
+ Intent intent = new Intent();
+ intent.setData(mCropPhotoUri);
+ mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_CANCELED, intent);
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
+ public void cropPhotoResultIsNotReturnedIfResultNotContent() {
+ Intent intent = new Intent();
+ intent.setData(Uri.parse("file://test"));
+ mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
+ public void cropDoesNotUseTakePhotoUri() throws IOException {
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
+
+ Intent startIntent = verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
+ }
+
+ @Test
+ public void internalCropUsedIfNoSystemCropperFound() throws IOException {
+ when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
+
+ new File(mImagesDir, "file.txt").createNewFile();
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
+
+ Intent startIntent = verifyStartSystemActivityForResult(
+ "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
+ assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
+
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
+
+ InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
+ Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
+ assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
+ assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
+ }
+
+ private Intent verifyStartActivityForResult(String action, int resultCode) {
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
+ .startActivityForResult(captor.capture(), eq(resultCode));
+ Intent intent = captor.getValue();
+ assertThat(intent.getAction()).isEqualTo(action);
+ return intent;
+ }
+
+ private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
+ .startSystemActivityForResult(captor.capture(), eq(resultCode));
+ Intent intent = captor.getValue();
+ assertThat(intent.getAction()).isEqualTo(action);
+ return intent;
+ }
+
+ private void saveBitmapToFile(File file) throws IOException {
+ Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
+ OutputStream os = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+ os.flush();
+ os.close();
+ }
+
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index a36a4493133c..c7673aa98797 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -197,5 +197,6 @@ public class SecureSettings {
Settings.Secure.LOCKSCREEN_SHOW_WALLET,
Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER,
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
+ Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index fc2c2339f022..42aa2053e1a4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -156,6 +156,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.LOCKSCREEN_SHOW_WALLET, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.STATUS_BAR_SHOW_VIBRATE_ICON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b1d00bb18e3..7f8b2f51754c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -154,7 +154,7 @@
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
- <!-- Needed for WallpaperManager.getWallpaperDimAmount in StatusBar.updateTheme -->
+ <!-- Needed for WallpaperManager.getWallpaperDimAmount in CentralSurfaces.updateTheme -->
<uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
<!-- Wifi Display -->
diff --git a/packages/SystemUI/docs/keyguard.md b/packages/SystemUI/docs/keyguard.md
index 5e7bc1c8cad2..8914042ee3cd 100644
--- a/packages/SystemUI/docs/keyguard.md
+++ b/packages/SystemUI/docs/keyguard.md
@@ -40,7 +40,7 @@ More coming
[1]: /frameworks/base/packages/SystemUI/docs/keyguard/bouncer.md
[2]: /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
-[3]: /frameworks/base/packages/SystemUI/docs/keyguard/aod.md
+[3]: /frameworks/base/packages/SystemUI/docs/keyguard/doze.md
[4]: /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
diff --git a/packages/SystemUI/docs/keyguard/aod.md b/packages/SystemUI/docs/keyguard/doze.md
index 7f89984abdb3..a6ccab9698d4 100644
--- a/packages/SystemUI/docs/keyguard/aod.md
+++ b/packages/SystemUI/docs/keyguard/doze.md
@@ -1,9 +1,10 @@
-# Always-on Display (AOD)
+# Doze
-AOD provides an alternatative 'screen-off' experience. Instead, of completely turning the display off, it provides a distraction-free, glanceable experience for the phone in a low-powered mode. In this low-powered mode, the display will have a lower refresh rate and the UI should frequently shift its displayed contents in order to prevent burn-in.
+Always-on Display (AOD) provides an alternative 'screen-off' experience. Instead, of completely turning the display off, it provides a distraction-free, glanceable experience for the phone in a low-powered mode. In this low-powered mode, the display will have a lower refresh rate and the UI should frequently shift its displayed contents in order to prevent burn-in. The recommended max on-pixel-ratio (OPR) is 5% to reduce battery consumption.
-The default doze component is specified by `config_dozeComponent` in the [framework config][1]. SystemUI provides a default Doze Component: [DozeService][2]. [DozeService][2] builds a [DozeMachine][3] with dependencies specified in [DozeModule][4] and configurations in [AmbientDisplayConfiguration][13] and [DozeParameters][14].
+The default doze component controls AOD and is specified by `config_dozeComponent` in the [framework config][1]. SystemUI provides a default Doze Component: [DozeService][2]. [DozeService][2] builds a [DozeMachine][3] with dependencies specified in [DozeModule][4] and configurations in [AmbientDisplayConfiguration][13] and [DozeParameters][14].
+Note: The default UI used in AOD shares views with the Lock Screen and does not create its own new views. Once dozing begins, [DozeUI][17] informs SystemUI's [DozeServiceHost][18] that dozing has begun - which sends this signal to relevant SystemUI Lock Screen views to animate accordingly. Within SystemUI, [StatusBarStateController][19] #isDozing and #getDozeAmount can be used to query dozing state.
[DozeMachine][3] handles the following main states:
* AOD - persistently showing UI when the device is in a low-powered state
* Pulsing - waking up the screen to show notifications (from AOD and screen off)
@@ -42,6 +43,22 @@ Relevant sensors include:
And are configured in the [AmbientDisplayConfiguration][13] with some related configurations specified in [DozeParameters][14].
+## Doze Suppressors
+When Dozing is enabled, it can still be suppressed based on the device state. On a high-level, doze and/or AOD may be suppressed if the device is:
+* in CAR_MODE
+* not provisioned
+* in power saver mode
+* being suppressed by an app (see [PowerManager#suppressAmbientDisplay][16])
+
+Refer to the documentation in [DozeSuppressors][15] for more information.
+
+## AOD burn-in and image retention
+Because AOD will show an image on the screen for an elogated period of time, AOD designs must take into consideration burn-in (leaving a permanent mark on the screen). Temporary burn-in is called image-retention.
+
+To prevent burn-in, it is recommended to often shift UI on the screen. [DozeUi][17] schedules a call to dozeTimeTick every minute to request a shift in UI for all elements on AOD. The amount of shift can be determined by undergoing simulated AOD testing since this may vary depending on the display.
+
+For manual local testing, set [DozeUI][17]#BURN_IN_TESTING_ENABLED to true, and then manual time broadcasts (ie: `adb shell 'date 022202222022.00 ; am broadcast -a android.intent.action.TIME_SET'`) will update the burn-in translations of the views. For a general idea where burn-in may be an issue, run the [software burn-in script][20].
+
## Debugging Tips
Enable DozeLog to print directly to logcat:
```
@@ -75,3 +92,9 @@ Other helpful dumpsys commands (`adb shell dumpsys <service>`):
[12]: /frameworks/base/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
[13]: /frameworks/base/core/java/android/hardware/display/AmbientDisplayConfiguration.java
[14]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+[15]: /frameworks/base/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
+[16]: /frameworks/base/core/java/android/os/PowerManager.java
+[17]: /frameworks/base/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+[18]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+[19]: /frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+[20]: /frameworks/base/packages/SystemUI/docs/clock-plugins.md
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6352f81b4474..c97ebe8d5559 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -3,7 +3,7 @@
-keep class com.android.systemui.recents.OverviewProxyRecentsImpl
-keep class com.android.systemui.statusbar.car.CarStatusBar
--keep class com.android.systemui.statusbar.phone.StatusBar
+-keep class com.android.systemui.statusbar.phone.CentralSurfaces
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 31d848d3e4d5..16a1d944c4d3 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -26,6 +26,7 @@
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
systemui:layout_constraintTop_toTopOf="parent"
+ android:layout_marginHorizontal="@dimen/status_view_margin_horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content">
<LinearLayout
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index 0ae5dc745478..5084ca48e608 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -1,254 +1 @@
-<?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.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector android:height="60dp" android:width="60dp" android:viewportHeight="60"
- android:viewportWidth="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071">
- <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30"
- android:translateY="38.75" android:scaleX="1" android:scaleY="1">
- <path android:name="_R_G_L_1_G_D_0_P_0"
- android:fillColor="@color/biometric_dialog_error"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/>
- </group>
- <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30"
- android:translateY="25" android:pivotX="0.002" android:pivotY="7.488"
- android:scaleX="1" android:scaleY="1">
- <path android:name="_R_G_L_1_G_D_1_P_0"
- android:fillColor="@color/biometric_dialog_error"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/>
- </group>
- <path android:name="_R_G_L_1_G_D_2_P_0"
- android:strokeColor="@color/biometric_dialog_error"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2.5" android:strokeAlpha="1"
- android:trimPathStart="0" android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/>
- </group>
- <group android:name="_R_G_L_0_G" android:translateX="-10.325"
- android:translateY="-10.25">
- <path android:name="_R_G_L_0_G_D_0_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="0" android:trimPathOffset="0"
- android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/>
- <path android:name="_R_G_L_0_G_D_1_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="0" android:trimPathOffset="0"
- android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/>
- <path android:name="_R_G_L_0_G_D_2_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="0" android:trimPathOffset="0"
- android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/>
- <path android:name="_R_G_L_0_G_D_3_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="0" android:trimPathOffset="0"
- android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/>
- </group>
- </group>
- <group android:name="time_group"/>
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="67"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="67"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="100"
- android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="100"
- android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="67"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="67"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="100"
- android:startOffset="67" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="100"
- android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_1_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathEnd" android:duration="67"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="trimPathEnd" android:duration="133"
- android:startOffset="67" android:valueFrom="1" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
- android:startOffset="83" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
- android:startOffset="83" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
- android:startOffset="83" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
- android:startOffset="83" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="translateX" android:duration="417"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0.975"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.659" android:translateY="15.75"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="167" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="167" android:startOffset="0" android:valueFrom="2.5" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="83" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0.975" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="417" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml
new file mode 100644
index 000000000000..c4f818146011
--- /dev/null
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml
@@ -0,0 +1 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0.975"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.75" android:translateY="15.75" android:pivotX="19.341" android:pivotY="24.25" android:scaleX="0.5" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="167" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="167" android:startOffset="0" android:valueFrom="2.5" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="83" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0.975" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="233" android:startOffset="0" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="267" android:startOffset="233" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="0.5" android:valueTo="0.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0.5" android:valueTo="0.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="167" android:valueFrom="0.5" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="167" android:valueFrom="0.5" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0" android:valueTo="0.5" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="683" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index fc2c7d00f3a7..c05a8d55c16c 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -1,247 +1 @@
-<?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.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector android:height="60dp" android:width="60dp" android:viewportHeight="60"
- android:viewportWidth="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071">
- <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30"
- android:translateY="38.75" android:scaleX="0" android:scaleY="0">
- <path android:name="_R_G_L_1_G_D_0_P_0"
- android:fillColor="@color/biometric_dialog_error"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/>
- </group>
- <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30"
- android:translateY="25" android:pivotX="0.002" android:pivotY="7.488"
- android:scaleX="1" android:scaleY="0">
- <path android:name="_R_G_L_1_G_D_1_P_0"
- android:fillColor="@color/biometric_dialog_error"
- android:fillAlpha="1" android:fillType="nonZero"
- android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/>
- </group>
- <path android:name="_R_G_L_1_G_D_2_P_0"
- android:strokeColor="@color/biometric_dialog_error"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2.5" android:strokeAlpha="1"
- android:trimPathStart="1" android:trimPathEnd="1"
- android:trimPathOffset="0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/>
- </group>
- <group android:name="_R_G_L_0_G" android:translateX="-10.325"
- android:translateY="-10.25">
- <path android:name="_R_G_L_0_G_D_0_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="1" android:trimPathOffset="0"
- android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/>
- <path android:name="_R_G_L_0_G_D_1_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="1" android:trimPathOffset="0"
- android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/>
- <path android:name="_R_G_L_0_G_D_2_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="1" android:trimPathOffset="0"
- android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/>
- <path android:name="_R_G_L_0_G_D_3_P_0"
- android:strokeColor="@color/biometric_dialog_accent"
- android:strokeLineCap="round" android:strokeLineJoin="round"
- android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
- android:trimPathEnd="1" android:trimPathOffset="0"
- android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/>
- </group>
- </group>
- <group android:name="time_group"/>
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="100"
- android:startOffset="167" android:valueFrom="0"
- android:valueTo="1.1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="100"
- android:startOffset="167" android:valueFrom="0"
- android:valueTo="1.1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="67"
- android:startOffset="267" android:valueFrom="1.1"
- android:valueTo="1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="67"
- android:startOffset="267" android:valueFrom="1.1"
- android:valueTo="1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="scaleX" android:duration="167"
- android:startOffset="0" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="100"
- android:startOffset="167" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="100"
- android:startOffset="167" android:valueFrom="0"
- android:valueTo="1.1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleX" android:duration="67"
- android:startOffset="267" android:valueFrom="1" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- <objectAnimator android:propertyName="scaleY" android:duration="67"
- android:startOffset="267" android:valueFrom="1.1"
- android:valueTo="1" android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_1_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathStart" android:duration="267"
- android:startOffset="0" android:valueFrom="1" android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathStart" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathStart" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathStart" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="trimPathStart" android:duration="167"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator android:propertyName="translateX" android:duration="350"
- android:startOffset="0" android:valueFrom="0" android:valueTo="1"
- android:valueType="floatType"/>
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0" android:strokeAlpha="0" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="0"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.659" android:translateY="15.75"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeWidth" android:duration="233" android:startOffset="67" android:valueFrom="0" android:valueTo="2.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="167" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="267" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="350" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml
new file mode 100644
index 000000000000..16944294a94e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml
@@ -0,0 +1 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="20.75" android:translateY="15.75"><path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group><group android:name="_R_G_L_0_G" android:translateX="37.357" android:translateY="43.25" android:pivotX="2.75" android:pivotY="2.75" android:scaleX="1.41866" android:scaleY="1.41866"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#d3e3fd" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="0" android:valueFrom="M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " android:valueTo="M30.81 32.26 C30.56,32.49 30.27,38.76 29.96,38.9 C29.77,39.46 29.13,39.94 28.57,40.26 C28.15,40.51 26.93,40.65 26.4,40.65 C26.18,40.65 11.91,40.62 11.71,40.58 C10.68,40.53 9.06,39.79 8.89,38.88 C8.6,38.74 8.34,32.48 8.1,32.27 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="143" android:startOffset="107" android:valueFrom="M30.81 32.26 C30.56,32.49 30.27,38.76 29.96,38.9 C29.77,39.46 29.13,39.94 28.57,40.26 C28.15,40.51 26.93,40.65 26.4,40.65 C26.18,40.65 11.91,40.62 11.71,40.58 C10.68,40.53 9.06,39.79 8.89,38.88 C8.6,38.74 8.34,32.48 8.1,32.27 " android:valueTo="M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.331,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="140" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="50" android:startOffset="140" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="0" android:valueFrom="M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " android:valueTo="M18.93 32.18 C17.11,32.68 16.62,30.26 16.62,30.26 C16.62,28.78 17.81,27.59 19.39,27.59 C20.96,27.59 22.15,28.78 22.15,30.26 C22.15,30.26 22.15,30.34 22.15,30.34 C22.15,30.89 21.11,32.54 19.57,32.19 C19.19,32.1 20.48,31.09 20.34,30.71 C20.34,30.71 20.02,29.88 20.02,29.88 C19.88,29.5 19.53,29.25 19.15,29.25 C18.63,29.25 18,29.67 18,30.22 C18,30.57 18.06,31.08 18.32,31.51 C18.49,31.8 19.02,32.25 19.79,32.04 C20.41,31.7 20.38,31.36 20.38,31.36 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="107" android:valueFrom="M18.93 32.18 C17.11,32.68 16.62,30.26 16.62,30.26 C16.62,28.78 17.81,27.59 19.39,27.59 C20.96,27.59 22.15,28.78 22.15,30.26 C22.15,30.26 22.15,30.34 22.15,30.34 C22.15,30.89 21.11,32.54 19.57,32.19 C19.19,32.1 20.48,31.09 20.34,30.71 C20.34,30.71 20.02,29.88 20.02,29.88 C19.88,29.5 19.53,29.25 19.15,29.25 C18.63,29.25 18,29.67 18,30.22 C18,30.57 18.06,31.08 18.32,31.51 C18.49,31.8 19.02,32.25 19.79,32.04 C20.41,31.7 20.38,31.36 20.38,31.36 " android:valueTo="M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="250" android:startOffset="0" android:valueFrom="M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " android:valueTo="M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.189,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="95" android:startOffset="0" android:valueFrom="M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " android:valueTo="M11.47 14.84 C11.47,14.84 12.21,11.43 13.54,9.84 C14.84,8.28 16.68,7.22 19.35,7.22 C22.01,7.22 23.98,8.4 25.19,10.18 C26.39,11.96 27.25,14.84 27.25,14.84 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="24" android:startOffset="95" android:valueFrom="M11.47 14.84 C11.47,14.84 12.21,11.43 13.54,9.84 C14.84,8.28 16.68,7.22 19.35,7.22 C22.01,7.22 23.98,8.4 25.19,10.18 C26.39,11.96 27.25,14.84 27.25,14.84 " android:valueTo="M12.11 16.85 C12.11,16.85 12.82,12.71 13.37,11.5 C14.17,9.24 16.38,7.53 19.35,7.53 C22.32,7.53 24.61,9.32 25.35,11.72 C25.61,12.64 26.62,16.85 26.62,16.85 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="81" android:startOffset="119" android:valueFrom="M12.11 16.85 C12.11,16.85 12.82,12.71 13.37,11.5 C14.17,9.24 16.38,7.53 19.35,7.53 C22.32,7.53 24.61,9.32 25.35,11.72 C25.61,12.64 26.62,16.85 26.62,16.85 " android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.261,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="233" android:startOffset="200" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.123,0 0.23,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="120" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="20" android:startOffset="120" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="120" android:startOffset="0" android:valueFrom="1.4186600000000003" android:valueTo="1.4186600000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="120" android:startOffset="0" android:valueFrom="1.4186600000000003" android:valueTo="1.4186600000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="130" android:startOffset="120" android:valueFrom="1.4186600000000003" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="130" android:startOffset="120" android:valueFrom="1.4186600000000003" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="517" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
deleted file mode 100644
index e456e2965d21..000000000000
--- a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:angle="90"
- android:startColor="#ff000000"
- android:endColor="#00000000"
- android:type="linear" />
-</shape>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 89690e8ff0ec..58adb9146bd0 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -49,8 +49,8 @@
<ImageView
android:id="@+id/biometric_icon"
- android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY" />
diff --git a/packages/SystemUI/res/layout/auth_biometric_face_to_fingerprint_view.xml b/packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml
index 7cf1789961d3..05ca2a786e3a 100644
--- a/packages/SystemUI/res/layout/auth_biometric_face_to_fingerprint_view.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml
@@ -14,12 +14,13 @@
~ limitations under the License.
-->
-<com.android.systemui.biometrics.AuthBiometricFaceToFingerprintView
+<com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceView
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/contents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/auth_biometric_contents"/>
-</com.android.systemui.biometrics.AuthBiometricFaceToFingerprintView> \ No newline at end of file
+</com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_udfps_view.xml b/packages/SystemUI/res/layout/auth_biometric_view.xml
index 238288eb9f69..ee4da25f2284 100644
--- a/packages/SystemUI/res/layout/auth_biometric_udfps_view.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_view.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
-<com.android.systemui.biometrics.AuthBiometricUdfpsView
+<com.android.systemui.biometrics.AuthBiometricView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contents"
android:layout_width="match_parent"
@@ -23,4 +23,4 @@
<include layout="@layout/auth_biometric_contents"/>
-</com.android.systemui.biometrics.AuthBiometricUdfpsView> \ No newline at end of file
+</com.android.systemui.biometrics.AuthBiometricView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 813787e8f9d0..1cbc3c284874 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -29,7 +29,6 @@
android:layout_width="@dimen/dream_overlay_notification_indicator_size"
android:layout_height="@dimen/dream_overlay_notification_indicator_size"
android:visibility="gone"
- android:contentDescription="@string/dream_overlay_status_bar_notification_indicator"
app:dotColor="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -68,7 +67,7 @@
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
- android:src="@drawable/ic_remove_circle"
+ android:src="@drawable/ic_qs_dnd_on"
android:tint="@android:color/white"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_priority_mode" />
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon.xml b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
deleted file mode 100644
index d4e98e217213..000000000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon.xml
+++ /dev/null
@@ -1,61 +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.
- -->
-
-<com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/foreground_service_dungeon"
- android:layout_width="@dimen/qs_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:visibility="visible"
->
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:gravity="bottom"
- android:visibility="visible"
- android:background="@drawable/notif_dungeon_bg_gradient"
- >
-
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/GM2_grey_200"
- android:visibility="visible"
- />
-
- <TextView
- android:id="@+id/dungeon_title"
- android:layout_height="48dp"
- android:layout_width="match_parent"
- android:padding="8dp"
- android:text="Apps active in background"
- android:textColor="@color/GM2_grey_200"
- />
-
- <!-- List containing the actual foreground service notifications -->
- <LinearLayout
- android:id="@+id/entry_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="bottom"
- android:orientation="vertical" >
- </LinearLayout>
-
- </LinearLayout>
-</com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
deleted file mode 100644
index a6f1638a1d89..000000000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
+++ /dev/null
@@ -1,43 +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.
- -->
-
-<com.android.systemui.statusbar.notification.row.DungeonRow
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/foreground_service_dungeon_row"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:padding="8dp"
- android:clickable="true"
- android:orientation="horizontal" >
-
- <com.android.systemui.statusbar.StatusBarIconView
- android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:padding="4dp" />
-
- <TextView
- android:id="@+id/app_name"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:gravity="center_vertical"
- android:layout_gravity="center_vertical"
- android:textColor="@color/GM2_grey_200"
- />
-
-</com.android.systemui.statusbar.notification.row.DungeonRow>
diff --git a/packages/SystemUI/res/layout/media_projection_dialog_title.xml b/packages/SystemUI/res/layout/media_projection_dialog_title.xml
deleted file mode 100644
index b9e39dae13dc..000000000000
--- a/packages/SystemUI/res/layout/media_projection_dialog_title.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 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.
--->
-
-<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:orientation="vertical">
- <ImageView
- android:id="@+id/dialog_icon"
- android:src="@drawable/ic_media_projection_permission"
- android:layout_height="24dp"
- android:layout_width="24dp"
- android:layout_marginTop="18dp"
- android:layout_marginBottom="12dp"
- android:layout_gravity="center_horizontal" />
- <TextView
- android:id="@+id/dialog_title"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Title" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index b2ff46e0de29..8ba1ff38fd63 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -27,7 +27,7 @@
<LinearLayout
android:id="@+id/half_shelf"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="bottom"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c7910afc73fc..e4706e263a0f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -94,10 +94,11 @@
<FrameLayout
android:id="@+id/qs_frame"
android:layout="@layout/qs_panel"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:clipChildren="false"
+ android:layout_marginHorizontal="@dimen/notification_panel_margin_horizontal"
systemui:viewType="com.android.systemui.plugins.qs.QS"
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
@@ -115,8 +116,9 @@
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
- android:layout_width="@dimen/notification_panel_width"
+ android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_marginHorizontal="@dimen/notification_panel_margin_horizontal"
android:layout_marginBottom="@dimen/notification_panel_margin_bottom"
android:importantForAccessibility="no"
systemui:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
index 65f55d0d5903..8a2310bc307d 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/plugin_frame"
android:theme="@style/Theme.SystemUI.QuickSettings"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/notification_side_paddings"
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 8a4516aaa49f..4f95811b21b3 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -46,4 +46,5 @@
the shade -->
<dimen name="lockscreen_shade_media_transition_distance">200dp</dimen>
+ <dimen name="notification_panel_margin_horizontal">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index da2403a96afa..56dc4a1026b5 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<resources>
- <!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
- <dimen name="notification_panel_width">504dp</dimen>
-
+ <dimen name="notification_panel_margin_horizontal">60dp</dimen>
+ <dimen name="status_view_margin_horizontal">62dp</dimen>
+ <dimen name="keyguard_clock_top_margin">40dp</dimen>
+ <dimen name="keyguard_status_view_bottom_margin">40dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index e897f75470a9..71c195896051 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -22,4 +22,6 @@
<dimen name="notification_panel_margin_bottom">56dp</dimen>
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
+
+ <dimen name="notification_panel_margin_horizontal">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
new file mode 100644
index 000000000000..594df346fab1
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <dimen name="status_view_margin_horizontal">124dp</dimen>
+ <dimen name="notification_panel_margin_horizontal">120dp</dimen>
+ <dimen name="keyguard_clock_top_margin">80dp</dimen>
+ <dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2de8324a506a..83e6a54e272b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -74,12 +74,12 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService)
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
</string>
<!-- The component name of the Safety Quick Settings Tile -->
<string name="safety_quick_settings_tile" translatable="false">
- custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService)
+ custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
</string>
<!-- The minimum number of tiles to display in QuickSettings -->
@@ -487,6 +487,9 @@
space -->
<bool name="config_showBatteryEstimateQSBH">false</bool>
+ <!-- Whether to show a severe low battery dialog. -->
+ <bool name="config_severe_battery_dialog">false</bool>
+
<!-- A path similar to frameworks/base/core/res/res/values/config.xml
config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 92bc8642b75c..5a7efca3dece 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,13 +388,10 @@
<dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
- <dimen name="notification_panel_width">@dimen/match_parent</dimen>
+ <dimen name="notification_panel_margin_horizontal">0dp</dimen>
<dimen name="brightness_mirror_height">48dp</dimen>
- <!-- The width of the panel that holds the quick settings. -->
- <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
-
<dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
<dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
@@ -879,7 +876,8 @@
<dimen name="remote_input_history_extra_height">60dp</dimen>
<!-- Biometric Dialog values -->
- <dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
+ <dimen name="biometric_dialog_face_icon_size">64dp</dimen>
+ <dimen name="biometric_dialog_fingerprint_icon_size">80dp</dimen>
<dimen name="biometric_dialog_button_negative_max_width">160dp</dimen>
<dimen name="biometric_dialog_button_positive_max_width">136dp</dimen>
<dimen name="biometric_dialog_corner_size">4dp</dimen>
@@ -1391,4 +1389,6 @@
<!-- The margin applied between complications -->
<dimen name="dream_overlay_complication_margin">0dp</dimen>
+
+ <dimen name="status_view_margin_horizontal">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index df16b0d45228..9e1f57bbfa75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -21,7 +21,13 @@
<string name="app_label">System UI</string>
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
- <string name="battery_low_title">Battery may run out soon</string>
+ <string name="battery_low_title">Turn on Battery Saver?</string>
+
+ <!-- When the battery is low, this is displayed to the user in a dialog. The description of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_description">You have <xliff:g id="percentage" example="20%">%s</xliff:g> battery left. Battery Saver turns on Dark theme, restricts background activity, and delays notifications.</string>
+
+ <!-- When the battery is low at first time, this is displayed to the user in a dialog. The description of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_intro">Battery Saver turns on Dark theme, restricts background activity, and delays notifications.</string>
<!-- A message that appears when the battery level is getting low in a dialog. This is
appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
@@ -316,6 +322,8 @@
<string name="biometric_dialog_face_icon_description_confirmed">Confirmed</string>
<!-- Message shown when a biometric is authenticated, waiting for the user to confirm authentication [CHAR LIMIT=40]-->
<string name="biometric_dialog_tap_confirm">Tap Confirm to complete</string>
+ <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
+ <string name="biometric_dialog_tap_confirm_with_face">Unlocked by your face. Press to continue.</string>
<!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_authenticated">Authenticated</string>
@@ -441,8 +449,8 @@
<string name="accessibility_quick_settings_dnd_none_on">total silence</string>
<!-- Content description of the do not disturb tile in quick settings when on in alarms only (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd_alarms_on">alarms only</string>
- <!-- Content description of the priority mode tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd" translatable="false">Priority mode.</string>
+ <!-- Content description of the do not disturb tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_dnd">Do Not Disturb.</string>
<!-- Content description of the bluetooth tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_bluetooth">Bluetooth.</string>
<!-- Content description of the bluetooth tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -520,8 +528,8 @@
<string name="ethernet_label">Ethernet</string>
<!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
- <!-- QuickSettings: Priority mode [CHAR LIMIT=NONE] -->
- <string name="quick_settings_dnd_label" translatable="false">Priority mode</string>
+ <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_dnd_label">Do Not Disturb</string>
<!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Alarms only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Total silence [CHAR LIMIT=NONE] -->
@@ -909,8 +917,8 @@
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
- <!-- The text to show in the notifications shade when Priority mode is suppressing notifications. [CHAR LIMIT=100] -->
- <string name="dnd_suppressing_shade_text" translatable="false">Notifications paused by Priority mode</string>
+ <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
+ <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
<!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
<string name="media_projection_action_text">Start now</string>
@@ -1329,8 +1337,8 @@
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
<string name="notification_channel_summary_priority_baseline">Shows at the top of conversation notifications and as a profile picture on lock screen</string>
<string name="notification_channel_summary_priority_bubble">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble</string>
- <string name="notification_channel_summary_priority_dnd" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Priority mode</string>
- <string name="notification_channel_summary_priority_all" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Priority mode</string>
+ <string name="notification_channel_summary_priority_dnd">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Do Not Disturb</string>
+ <string name="notification_channel_summary_priority_all">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb</string>
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
<string name="notification_priority_title">Priority</string>
@@ -1521,8 +1529,8 @@
<!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
<string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
- <!-- SysUI Tuner: Label for screen about priority mode settings [CHAR LIMIT=60] -->
- <string name="volume_and_do_not_disturb" translatable="false">Priority mode</string>
+ <!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
+ <string name="volume_and_do_not_disturb">Do Not Disturb</string>
<!-- SysUI Tuner: Switch to control whether volume buttons enter/exit do
not disturb [CHAR LIMIT=60] -->
@@ -1878,17 +1886,17 @@
<!-- Label for when bluetooth is off in QS detail panel [CHAR LIMIT=NONE] -->
<string name="bt_is_off">Bluetooth is off</string>
- <!-- Label for when Priority mode is off in QS detail panel [CHAR LIMIT=NONE] -->
- <string name="dnd_is_off" translatable="false">Priority mode is off</string>
+ <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] -->
+ <string name="dnd_is_off">Do Not Disturb is off</string>
- <!-- Prompt for when Priority mode is on from automatic rule in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_auto_rule" translatable="false">Priority mode was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
+ <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
- <!-- Prompt for when Priority mode is on from app in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_app" translatable="false">Priority mode was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
+ <!-- Prompt for when Do not disturb is on from app in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_app">Do Not Disturb was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
- <!-- Prompt for when Priority mode is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_auto_rule_app" translatable="false">Priority mode was turned on by an automatic rule or app.</string>
+ <!-- Prompt for when Do not disturb is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_auto_rule_app">Do Not Disturb was turned on by an automatic rule or app.</string>
<!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_title">Apps running in background</string>
@@ -2284,8 +2292,8 @@
<string name="people_tile_description">See recent messages, missed calls, and status updates</string>
<!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
<string name="people_tile_title">Conversation</string>
- <!-- Text when the Conversation widget when Priority mode is suppressing the notification. [CHAR LIMIT=50] -->
- <string name="paused_by_dnd" translatable="false">Paused by Priority mode</string>
+ <!-- Text when the Conversation widget when Do Not Disturb is suppressing the notification. [CHAR LIMIT=50] -->
+ <string name="paused_by_dnd">Paused by Do Not Disturb</string>
<!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] -->
<string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message: <xliff:g id="notification" example="Hey! How is your day going">%2$s</xliff:g></string>
<!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] -->
@@ -2421,6 +2429,9 @@
<string name="dream_overlay_status_bar_assistant_guest_mode_enabled">Assistant guest mode enabled</string>
<!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
- <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
- <string name="dream_overlay_status_bar_notification_indicator">There are notifications</string>
+ <!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_notification_indicator">{count, plural,
+ =1 {# notification}
+ other {# notifications}
+ }</string>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index bf23a49df2c3..ba0a6d149707 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import android.os.Binder;
@@ -137,14 +136,6 @@ public class InputConsumerController {
* Registers the input consumer.
*/
public void registerInputConsumer() {
- registerInputConsumer(false);
- }
-
- /**
- * Registers the input consumer.
- * @param withSfVsync the flag set using sf vsync signal or no
- */
- public void registerInputConsumer(boolean withSfVsync) {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
@@ -154,7 +145,7 @@ public class InputConsumerController {
Log.e(TAG, "Failed to create input consumer", e);
}
mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
- withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
+ Choreographer.getInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a72a050ee023..31f466f0fdf9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
@@ -27,7 +25,6 @@ import android.widget.GridLayout;
import androidx.core.graphics.ColorUtils;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -44,9 +41,6 @@ public class KeyguardStatusView extends GridLayout {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
- private final LockPatternUtils mLockPatternUtils;
- private final IActivityManager mIActivityManager;
-
private ViewGroup mStatusViewContainer;
private KeyguardClockSwitch mClockView;
private KeyguardSliceView mKeyguardSlice;
@@ -56,14 +50,6 @@ public class KeyguardStatusView extends GridLayout {
private int mTextColor;
private float mChildrenAlphaExcludingSmartSpace = 1f;
- /**
- * Bottom margin that defines the margin between bottom of smart space and top of notification
- * icons on AOD.
- */
- private int mIconTopMargin;
- private int mIconTopMarginWithHeader;
- private boolean mShowingHeader;
-
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -74,8 +60,6 @@ public class KeyguardStatusView extends GridLayout {
public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mIActivityManager = ActivityManager.getService();
- mLockPatternUtils = new LockPatternUtils(getContext());
}
@Override
@@ -91,25 +75,11 @@ public class KeyguardStatusView extends GridLayout {
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mTextColor = mClockView.getCurrentTextColor();
- mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
- onSliceContentChanged();
-
mMediaHostContainer = findViewById(R.id.status_view_media_container);
updateDark();
}
- /**
- * Moves clock, adjusting margins when slice content changes.
- */
- private void onSliceContentChanged() {
- final boolean hasHeader = mKeyguardSlice.hasHeader();
- if (mShowingHeader == hasHeader) {
- return;
- }
- mShowingHeader = hasHeader;
- }
-
void setDarkAmount(float darkAmount) {
if (mDarkAmount == darkAmount) {
return;
@@ -158,10 +128,4 @@ public class KeyguardStatusView extends GridLayout {
mKeyguardSlice.dump(fd, pw, args);
}
}
-
- private void loadBottomMargin() {
- mIconTopMargin = getResources().getDimensionPixelSize(R.dimen.widget_vertical_padding);
- mIconTopMarginWithHeader = getResources().getDimensionPixelSize(
- R.dimen.widget_vertical_padding_with_header);
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 122f3d7f23f1..295d77d55b56 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -24,9 +24,9 @@ import androidx.annotation.Nullable;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -176,9 +176,9 @@ public interface KeyguardViewController {
// achieving complete abstraction away from where the Keyguard View is mounted.
/**
- * Registers the StatusBar to which this Keyguard View is mounted.
+ * Registers the CentralSurfaces to which this Keyguard View is mounted.
*/
- void registerStatusBar(StatusBar statusBar,
+ void registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController notificationPanelViewController,
@Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 4ad51835687f..370686a1e682 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,7 +60,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
@@ -78,7 +78,7 @@ import javax.inject.Inject;
* For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
* icon will show a set distance from the bottom of the device.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
private static final String TAG = "LockIconViewController";
private static final float sDefaultDensity =
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
index 207ac2852f2f..8dbe5e00ab7e 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Retention;
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
index ba0642f57a88..f498ef3466ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Retention;
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
index 880822aa7343..aeae8e3d4b27 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Retention;
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index cc166c210078..5bd620e873b0 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -23,7 +23,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -33,17 +33,17 @@ import dagger.Lazy;
/**
* Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
- * delegates to an actual implementation (StatusBar).
+ * delegates to an actual implementation (CentralSurfaces).
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@SysUISingleton
public class ActivityStarterDelegate implements ActivityStarter {
- private Lazy<Optional<StatusBar>> mActualStarterOptionalLazy;
+ private Lazy<Optional<CentralSurfaces>> mActualStarterOptionalLazy;
@Inject
- public ActivityStarterDelegate(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
- mActualStarterOptionalLazy = statusBarOptionalLazy;
+ public ActivityStarterDelegate(Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
+ mActualStarterOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 881e6a917a45..bd8e44ceab80 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -54,7 +54,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.util.Assert;
@@ -180,7 +180,7 @@ public class SystemActions extends CoreStartable {
private final Optional<Recents> mRecentsOptional;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final NotificationShadeWindowController mNotificationShadeController;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@@ -188,7 +188,7 @@ public class SystemActions extends CoreStartable {
@Inject
public SystemActions(Context context,
NotificationShadeWindowController notificationShadeController,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Optional<Recents> recentsOptional) {
super(context);
mRecentsOptional = recentsOptional;
@@ -201,7 +201,7 @@ public class SystemActions extends CoreStartable {
// NotificationShadeWindowController.registerCallback() only keeps weak references.
mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
registerOrUnregisterDismissNotificationShadeAction();
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
@@ -311,9 +311,10 @@ public class SystemActions extends CoreStartable {
// Saving state in instance variable since this callback is called quite often to avoid
// binder calls
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.map(StatusBar::isPanelExpanded).orElse(false)
- && !statusBarOptional.get().isKeyguardShowing()) {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.map(CentralSurfaces::isPanelExpanded).orElse(false)
+ && !centralSurfacesOptional.get().isKeyguardShowing()) {
if (!mDismissNotificationShadeActionRegistered) {
mA11yManager.registerSystemAction(
createRemoteAction(
@@ -466,12 +467,12 @@ public class SystemActions extends CoreStartable {
}
private void handleNotifications() {
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::animateExpandNotificationsPanel);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::animateExpandNotificationsPanel);
}
private void handleQuickSettings() {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.animateExpandSettingsPanel(null));
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null));
}
private void handlePowerDialog() {
@@ -524,8 +525,8 @@ public class SystemActions extends CoreStartable {
}
private void handleAccessibilityDismissNotificationShade() {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.animateCollapsePanels(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 885a1777f30b..aafbf7e4d439 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -172,6 +172,17 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
}
@MainThread
+ void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
+ callback);
+ }
+ }
+
+ @MainThread
void disableWindowMagnification(int displayId,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
@@ -210,9 +221,9 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
}
@Override
- public void onDrag(int displayId) {
+ public void onMove(int displayId) {
if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onDrag(displayId);
+ mWindowMagnificationConnectionImpl.onMove(displayId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index dc1e0054ff24..3b4114bfb984 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -156,6 +156,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
}
mAnimationCallback = animationCallback;
setupEnableAnimationSpecs(scale, centerX, centerY);
+
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
mController.enableWindowMagnificationInternal(scale, centerX, centerY,
@@ -178,6 +179,24 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
}
}
+ void moveWindowMagnifierToPosition(float centerX, float centerY,
+ IRemoteMagnificationAnimationCallback callback) {
+ if (mState == STATE_ENABLED) {
+ // We set the animation duration to shortAnimTime which would be reset at the end.
+ mValueAnimator.setDuration(mContext.getResources()
+ .getInteger(com.android.internal.R.integer.config_shortAnimTime));
+ enableWindowMagnification(Float.NaN, centerX, centerY,
+ /* magnificationFrameOffsetRatioX */ Float.NaN,
+ /* magnificationFrameOffsetRatioY */ Float.NaN, callback);
+ } else if (mState == STATE_ENABLING) {
+ sendAnimationCallback(false);
+ mAnimationCallback = callback;
+ mValueAnimator.setDuration(mContext.getResources()
+ .getInteger(com.android.internal.R.integer.config_shortAnimTime));
+ setupEnableAnimationSpecs(Float.NaN, centerX, centerY);
+ }
+ }
+
private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) {
if (mController == null) {
return;
@@ -193,9 +212,16 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
R.integer.magnification_default_scale) : scale, centerX, centerY);
} else {
mStartSpec.set(currentScale, currentCenterX, currentCenterY);
- mEndSpec.set(Float.isNaN(scale) ? currentScale : scale,
- Float.isNaN(centerX) ? currentCenterX : centerX,
- Float.isNaN(centerY) ? currentCenterY : centerY);
+
+ final float endScale = (mState == STATE_ENABLING ? mEndSpec.mScale : currentScale);
+ final float endCenterX =
+ (mState == STATE_ENABLING ? mEndSpec.mCenterX : currentCenterX);
+ final float endCenterY =
+ (mState == STATE_ENABLING ? mEndSpec.mCenterY : currentCenterY);
+
+ mEndSpec.set(Float.isNaN(scale) ? endScale : scale,
+ Float.isNaN(centerX) ? endCenterX : centerX,
+ Float.isNaN(centerY) ? endCenterY : centerY);
}
if (DEBUG) {
Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = "
@@ -269,6 +295,9 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
setState(STATE_ENABLED);
}
sendAnimationCallback(true);
+ // We reset the duration to config_longAnimTime
+ mValueAnimator.setDuration(mContext.getResources()
+ .getInteger(com.android.internal.R.integer.config_longAnimTime));
}
@Override
@@ -313,10 +342,10 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
}
- private static ValueAnimator newValueAnimator(Resources resources) {
+ private static ValueAnimator newValueAnimator(Resources resource) {
final ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(
- resources.getInteger(com.android.internal.R.integer.config_longAnimTime));
+ resource.getInteger(com.android.internal.R.integer.config_longAnimTime));
valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
valueAnimator.setFloatValues(0.0f, 1.0f);
return valueAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 1d22633455e9..aa684faee5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -77,6 +77,13 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
@Override
+ public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ mHandler.post(() -> mWindowMagnification.moveWindowMagnifierToPositionInternal(
+ displayId, positionX, positionY, callback));
+ }
+
+ @Override
public void showMagnificationButton(int displayId, int magnificationMode) {
mHandler.post(
() -> mModeSwitchesController.showButton(displayId, magnificationMode));
@@ -143,10 +150,10 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
}
- void onDrag(int displayId) {
+ void onMove(int displayId) {
if (mConnectionCallback != null) {
try {
- mConnectionCallback.onDrag(displayId);
+ mConnectionCallback.onMove(displayId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to inform taking control by a user", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index de03993a6f17..50ca447090b5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -852,6 +852,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@Override
public void move(int xOffset, int yOffset) {
moveWindowMagnifier(xOffset, yOffset);
+ mWindowMagnifierCallback.onMove(mDisplayId);
}
/**
@@ -985,6 +986,14 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
+ void moveWindowMagnifierToPosition(float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ if (mMirrorSurfaceView == null) {
+ return;
+ }
+ mAnimationController.moveWindowMagnifierToPosition(positionX, positionY, callback);
+ }
+
/**
* Gets the scale.
*
@@ -1037,8 +1046,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@Override
public boolean onDrag(float offsetX, float offsetY) {
- moveWindowMagnifier(offsetX, offsetY);
- mWindowMagnifierCallback.onDrag(mDisplayId);
+ move((int) offsetX, (int) offsetY);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index bdded10dfa1d..c334ca664c46 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -17,7 +17,6 @@
package com.android.systemui.accessibility;
import android.graphics.Rect;
-import android.view.ViewConfiguration;
/**
* A callback to inform {@link com.android.server.accessibility.AccessibilityManagerService} about
@@ -56,11 +55,9 @@ interface WindowMagnifierCallback {
void onAccessibilityActionPerformed(int displayId);
/**
- * Called when the user is performing dragging gesture. It is started after the offset
- * between the down location and the move event location exceed
- * {@link ViewConfiguration#getScaledTouchSlop()}.
+ * Called when the user is performing a move action.
*
* @param displayId The logical display id.
*/
- void onDrag(int displayId);
+ void onMove(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index aedaf968ac7d..dfff00b90ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -36,7 +36,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.ArrayList;
import java.util.List;
@@ -69,7 +69,7 @@ public final class PhoneStateMonitor {
};
private final Context mContext;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final StatusBarStateController mStatusBarStateController;
private boolean mLauncherShowing;
@@ -77,10 +77,11 @@ public final class PhoneStateMonitor {
@Inject
PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ BootCompleteCache bootCompleteCache,
StatusBarStateController statusBarStateController) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mStatusBarStateController = statusBarStateController;
mDefaultHome = getCurrentDefaultHome();
@@ -180,7 +181,8 @@ public final class PhoneStateMonitor {
}
private boolean isBouncerShowing() {
- return mStatusBarOptionalLazy.get().map(StatusBar::isBouncerShowing).orElse(false);
+ return mCentralSurfacesOptionalLazy.get()
+ .map(CentralSurfaces::isBouncerShowing).orElse(false);
}
private boolean isKeyguardLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
new file mode 100644
index 000000000000..55611f7d7ada
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.Log
+import android.widget.ImageView
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthBiometricView.BiometricState
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN
+import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
+import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
+import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
+import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
+
+private const val TAG = "AuthBiometricFaceIconController"
+
+/** Face only icon animator for BiometricPrompt. */
+class AuthBiometricFaceIconController(
+ context: Context,
+ iconView: ImageView
+) : AuthIconController(context, iconView) {
+
+ // false = dark to light, true = light to dark
+ private var lastPulseLightToDark = false
+
+ @BiometricState
+ private var state = 0
+
+ init {
+ val size = context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
+ iconView.layoutParams.width = size
+ iconView.layoutParams.height = size
+ showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light)
+ }
+
+ private fun startPulsing() {
+ lastPulseLightToDark = false
+ animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true)
+ }
+
+ private fun pulseInNextDirection() {
+ val iconRes = if (lastPulseLightToDark) {
+ R.drawable.face_dialog_pulse_dark_to_light
+ } else {
+ R.drawable.face_dialog_pulse_light_to_dark
+ }
+ animateIcon(iconRes, true /* repeat */)
+ lastPulseLightToDark = !lastPulseLightToDark
+ }
+
+ override fun handleAnimationEnd(drawable: Drawable) {
+ if (state == STATE_AUTHENTICATING || state == STATE_HELP) {
+ pulseInNextDirection()
+ }
+ }
+
+ override fun updateIcon(@BiometricState oldState: Int, @BiometricState newState: Int) {
+ val lastStateIsErrorIcon = (oldState == STATE_ERROR || oldState == STATE_HELP)
+ if (newState == STATE_AUTHENTICATING_ANIMATING_IN) {
+ showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_authenticating
+ )
+ } else if (newState == STATE_AUTHENTICATING) {
+ startPulsing()
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_authenticating
+ )
+ } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
+ animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_confirmed
+ )
+ } else if (lastStateIsErrorIcon && newState == STATE_IDLE) {
+ animateIconOnce(R.drawable.face_dialog_error_to_idle)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_idle
+ )
+ } else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) {
+ animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated
+ )
+ } else if (newState == STATE_ERROR && oldState != STATE_ERROR) {
+ animateIconOnce(R.drawable.face_dialog_dark_to_error)
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
+ animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated
+ )
+ } else if (newState == STATE_PENDING_CONFIRMATION) {
+ animateIconOnce(R.drawable.face_dialog_wink_from_dark)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated
+ )
+ } else if (newState == STATE_IDLE) {
+ showStaticDrawable(R.drawable.face_dialog_idle_static)
+ iconView.contentDescription = context.getString(
+ R.string.biometric_dialog_face_icon_description_idle
+ )
+ } else {
+ Log.w(TAG, "Unhandled state: $newState")
+ }
+ state = newState
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
deleted file mode 100644
index ae3e94b9a1cb..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
+++ /dev/null
@@ -1,245 +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 android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator.Modality;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-
-/**
- * Manages the layout of an auth dialog for devices with both a face sensor and a fingerprint
- * sensor. Face authentication is attempted first, followed by fingerprint if the initial attempt is
- * unsuccessful.
- */
-public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
- private static final String TAG = "BiometricPrompt/AuthBiometricFaceToFingerprintView";
-
- protected static class UdfpsIconController extends IconController {
- @BiometricState private int mIconState = STATE_IDLE;
-
- protected UdfpsIconController(
- @NonNull Context context, @NonNull ImageView iconView, @NonNull TextView textView) {
- super(context, iconView, textView);
- }
-
- void updateState(@BiometricState int newState) {
- updateState(mIconState, newState);
- }
-
- @Override
- protected void updateState(int lastState, int newState) {
- final boolean lastStateIsErrorIcon =
- lastState == STATE_ERROR || lastState == STATE_HELP;
-
- switch (newState) {
- case STATE_IDLE:
- case STATE_AUTHENTICATING_ANIMATING_IN:
- case STATE_AUTHENTICATING:
- case STATE_PENDING_CONFIRMATION:
- case STATE_AUTHENTICATED:
- if (lastStateIsErrorIcon) {
- animateOnce(R.drawable.fingerprint_dialog_error_to_fp);
- } else {
- showStaticDrawable(R.drawable.fingerprint_dialog_fp_to_error);
- }
- mIconView.setContentDescription(mContext.getString(
- R.string.accessibility_fingerprint_dialog_fingerprint_icon));
- break;
-
- case STATE_ERROR:
- case STATE_HELP:
- if (!lastStateIsErrorIcon) {
- animateOnce(R.drawable.fingerprint_dialog_fp_to_error);
- } else {
- showStaticDrawable(R.drawable.fingerprint_dialog_error_to_fp);
- }
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_try_again));
- break;
-
- default:
- Log.e(TAG, "Unknown biometric dialog state: " + newState);
- break;
- }
-
- mState = newState;
- mIconState = newState;
- }
- }
-
- @Modality private int mActiveSensorType = TYPE_FACE;
- @Nullable private ModalityListener mModalityListener;
- @Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
- @Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
- @Nullable @VisibleForTesting UdfpsIconController mUdfpsIconController;
-
-
- public AuthBiometricFaceToFingerprintView(Context context) {
- super(context);
- }
-
- public AuthBiometricFaceToFingerprintView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @VisibleForTesting
- AuthBiometricFaceToFingerprintView(Context context, AttributeSet attrs, Injector injector) {
- super(context, attrs, injector);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mUdfpsIconController = new UdfpsIconController(mContext, mIconView, mIndicatorView);
- }
-
- @Modality
- int getActiveSensorType() {
- return mActiveSensorType;
- }
-
- boolean isFingerprintUdfps() {
- return mFingerprintSensorProps.isAnyUdfpsType();
- }
-
- void setModalityListener(@NonNull ModalityListener listener) {
- mModalityListener = listener;
- }
-
- void setFingerprintSensorProps(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
- mFingerprintSensorProps = sensorProps;
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return mActiveSensorType == TYPE_FINGERPRINT ? 0
- : super.getDelayAfterAuthenticatedDurationMs();
- }
-
- @Override
- protected boolean supportsManualRetry() {
- return false;
- }
-
- @Override
- public void onAuthenticationFailed(
- @Modality int modality, @Nullable String failureReason) {
- super.onAuthenticationFailed(modality, checkErrorForFallback(failureReason));
- }
-
- @Override
- public void onError(int modality, String error) {
- super.onError(modality, checkErrorForFallback(error));
- }
-
- private String checkErrorForFallback(String message) {
- if (mActiveSensorType == TYPE_FACE) {
- Log.d(TAG, "Falling back to fingerprint: " + message);
-
- // switching from face -> fingerprint mode, suppress root error messages
- mCallback.onAction(Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR);
- return mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead);
- }
- return message;
- }
-
- @Override
- @BiometricState
- protected int getStateForAfterError() {
- if (mActiveSensorType == TYPE_FACE) {
- return STATE_AUTHENTICATING;
- }
-
- return super.getStateForAfterError();
- }
-
- @Override
- public void updateState(@BiometricState int newState) {
- if (mActiveSensorType == TYPE_FACE) {
- if (newState == STATE_HELP || newState == STATE_ERROR) {
- mActiveSensorType = TYPE_FINGERPRINT;
-
- setRequireConfirmation(false);
- mConfirmButton.setEnabled(false);
- mConfirmButton.setVisibility(View.GONE);
-
- if (mModalityListener != null) {
- mModalityListener.onModalitySwitched(TYPE_FACE, mActiveSensorType);
- }
-
- // Deactivate the face icon controller so it stops drawing to the view
- mFaceIconController.deactivate();
- // Then, activate this icon controller. We need to start in the "idle" state
- mUdfpsIconController.updateState(STATE_IDLE);
- }
- } else { // Fingerprint
- mUdfpsIconController.updateState(newState);
- }
-
- super.updateState(newState);
- }
-
- @Override
- @NonNull
- AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
- final AuthDialog.LayoutParams layoutParams = super.onMeasureInternal(width, height);
- return isFingerprintUdfps()
- ? getUdfpsMeasureAdapter().onMeasureInternal(width, height, layoutParams)
- : layoutParams;
- }
-
- @NonNull
- private UdfpsDialogMeasureAdapter getUdfpsMeasureAdapter() {
- if (mUdfpsMeasureAdapter == null
- || mUdfpsMeasureAdapter.getSensorProps() != mFingerprintSensorProps) {
- mUdfpsMeasureAdapter = new UdfpsDialogMeasureAdapter(this, mFingerprintSensorProps);
- }
- return mUdfpsMeasureAdapter;
- }
-
- @Override
- public void onSaveState(@NonNull Bundle outState) {
- super.onSaveState(outState);
- outState.putInt(AuthDialog.KEY_BIOMETRIC_SENSOR_TYPE, mActiveSensorType);
- outState.putParcelable(AuthDialog.KEY_BIOMETRIC_SENSOR_PROPS, mFingerprintSensorProps);
- }
-
- @Override
- public void restoreState(@Nullable Bundle savedState) {
- super.restoreState(savedState);
- if (savedState != null) {
- mActiveSensorType = savedState.getInt(AuthDialog.KEY_BIOMETRIC_SENSOR_TYPE, TYPE_FACE);
- mFingerprintSensorProps =
- savedState.getParcelable(AuthDialog.KEY_BIOMETRIC_SENSOR_PROPS);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
deleted file mode 100644
index 48f6431aec69..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * 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.biometrics;
-
-import android.content.Context;
-import android.graphics.drawable.Animatable2;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricAuthenticator.Modality;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-
-public class AuthBiometricFaceView extends AuthBiometricView {
-
- private static final String TAG = "BiometricPrompt/AuthBiometricFaceView";
-
- // Delay before dismissing after being authenticated/confirmed.
- private static final int HIDE_DELAY_MS = 500;
-
- protected static class IconController extends Animatable2.AnimationCallback {
- protected Context mContext;
- protected ImageView mIconView;
- protected TextView mTextView;
- protected Handler mHandler;
- protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
- protected @BiometricState int mState;
- protected boolean mDeactivated;
-
- protected IconController(Context context, ImageView iconView, TextView textView) {
- mContext = context;
- mIconView = iconView;
- mTextView = textView;
- mHandler = new Handler(Looper.getMainLooper());
- showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light);
- }
-
- protected void animateOnce(int iconRes) {
- animateIcon(iconRes, false);
- }
-
- protected void showStaticDrawable(int iconRes) {
- mIconView.setImageDrawable(mContext.getDrawable(iconRes));
- }
-
- protected void animateIcon(int iconRes, boolean repeat) {
- Log.d(TAG, "animateIcon, state: " + mState + ", deactivated: " + mDeactivated);
- if (mDeactivated) {
- return;
- }
-
- final AnimatedVectorDrawable icon =
- (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
- mIconView.setImageDrawable(icon);
- icon.forceAnimationOnUI();
- if (repeat) {
- icon.registerAnimationCallback(this);
- }
- icon.start();
- }
-
- protected void startPulsing() {
- mLastPulseLightToDark = false;
- animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true);
- }
-
- protected void pulseInNextDirection() {
- int iconRes = mLastPulseLightToDark ? R.drawable.face_dialog_pulse_dark_to_light
- : R.drawable.face_dialog_pulse_light_to_dark;
- animateIcon(iconRes, true /* repeat */);
- mLastPulseLightToDark = !mLastPulseLightToDark;
- }
-
- @Override
- public void onAnimationEnd(Drawable drawable) {
- super.onAnimationEnd(drawable);
- Log.d(TAG, "onAnimationEnd, mState: " + mState + ", deactivated: " + mDeactivated);
- if (mDeactivated) {
- return;
- }
-
- if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
- pulseInNextDirection();
- }
- }
-
- protected void deactivate() {
- mDeactivated = true;
- }
-
- protected void updateState(int lastState, int newState) {
- if (mDeactivated) {
- Log.w(TAG, "Ignoring updateState when deactivated: " + newState);
- return;
- }
-
- final boolean lastStateIsErrorIcon =
- lastState == STATE_ERROR || lastState == STATE_HELP;
-
- if (newState == STATE_AUTHENTICATING_ANIMATING_IN) {
- showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticating));
- } else if (newState == STATE_AUTHENTICATING) {
- startPulsing();
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticating));
- } else if (lastState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
- animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_confirmed));
- } else if (lastStateIsErrorIcon && newState == STATE_IDLE) {
- animateOnce(R.drawable.face_dialog_error_to_idle);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_idle));
- } else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) {
- animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_ERROR && lastState != STATE_ERROR) {
- animateOnce(R.drawable.face_dialog_dark_to_error);
- } else if (lastState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
- animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_PENDING_CONFIRMATION) {
- animateOnce(R.drawable.face_dialog_wink_from_dark);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_IDLE) {
- showStaticDrawable(R.drawable.face_dialog_idle_static);
- mIconView.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_idle));
- } else {
- Log.w(TAG, "Unhandled state: " + newState);
- }
- mState = newState;
- }
- }
-
- @Nullable @VisibleForTesting IconController mFaceIconController;
- @NonNull private final OnAttachStateChangeListener mOnAttachStateChangeListener =
- new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
-
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mFaceIconController.deactivate();
- }
- };
-
- public AuthBiometricFaceView(Context context) {
- this(context, null);
- }
-
- public AuthBiometricFaceView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @VisibleForTesting
- AuthBiometricFaceView(Context context, AttributeSet attrs, Injector injector) {
- super(context, attrs, injector);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
-
- addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return HIDE_DELAY_MS;
- }
-
- @Override
- protected int getStateForAfterError() {
- return STATE_IDLE;
- }
-
- @Override
- protected void handleResetAfterError() {
- resetErrorView();
- }
-
- @Override
- protected void handleResetAfterHelp() {
- resetErrorView();
- }
-
- @Override
- protected boolean supportsSmallDialog() {
- return true;
- }
-
- @Override
- protected boolean supportsManualRetry() {
- return true;
- }
-
- @Override
- public void updateState(@BiometricState int newState) {
- mFaceIconController.updateState(mState, newState);
-
- if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
- (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
- resetErrorView();
- }
-
- // Do this last since the state variable gets updated.
- super.updateState(newState);
- }
-
- @Override
- public void onAuthenticationFailed(@Modality int modality, @Nullable String failureReason) {
- if (getSize() == AuthDialog.SIZE_MEDIUM) {
- if (supportsManualRetry()) {
- mTryAgainButton.setVisibility(View.VISIBLE);
- mConfirmButton.setVisibility(View.GONE);
- }
- }
-
- // Do this last since we want to know if the button is being animated (in the case of
- // small -> medium dialog)
- super.onAuthenticationFailed(modality, failureReason);
- }
-
- private void resetErrorView() {
- mIndicatorView.setTextColor(mTextColorHint);
- mIndicatorView.setVisibility(View.INVISIBLE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt
new file mode 100644
index 000000000000..be89d10393dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.biometrics
+
+import android.content.Context
+import android.hardware.biometrics.BiometricAuthenticator.Modality
+import android.util.AttributeSet
+
+/** Face only view for BiometricPrompt. */
+class AuthBiometricFaceView(
+ context: Context,
+ attrs: AttributeSet? = null
+) : AuthBiometricView(context, attrs) {
+
+ override fun getDelayAfterAuthenticatedDurationMs() = HIDE_DELAY_MS
+
+ override fun getStateForAfterError() = STATE_IDLE
+
+ override fun handleResetAfterError() = resetErrorView()
+
+ override fun handleResetAfterHelp() = resetErrorView()
+
+ override fun supportsSmallDialog() = true
+
+ override fun supportsManualRetry() = true
+
+ override fun supportsRequireConfirmation() = true
+
+ override fun createIconController(): AuthIconController =
+ AuthBiometricFaceIconController(mContext, mIconView)
+
+ override fun updateState(@BiometricState newState: Int) {
+ if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
+ newState == STATE_AUTHENTICATING && size == AuthDialog.SIZE_MEDIUM) {
+ resetErrorView()
+ }
+
+ // Do this last since the state variable gets updated.
+ super.updateState(newState)
+ }
+
+ override fun onAuthenticationFailed(
+ @Modality modality: Int,
+ failureReason: String?
+ ) {
+ if (size == AuthDialog.SIZE_MEDIUM) {
+ if (supportsManualRetry()) {
+ mTryAgainButton.visibility = VISIBLE
+ mConfirmButton.visibility = GONE
+ }
+ }
+
+ // Do this last since we want to know if the button is being animated (in the case of
+ // small -> medium dialog)
+ super.onAuthenticationFailed(modality, failureReason)
+ }
+
+ private fun resetErrorView() {
+ mIndicatorView.setTextColor(mTextColorHint)
+ mIndicatorView.visibility = INVISIBLE
+ }
+
+ companion object {
+ /** Delay before dismissing after being authenticated/confirmed. */
+ const val HIDE_DELAY_MS = 500
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
new file mode 100644
index 000000000000..3e4e573c9531
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthBiometricView.BiometricState
+import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
+import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
+import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
+
+/** Face/Fingerprint combined icon animator for BiometricPrompt. */
+class AuthBiometricFingerprintAndFaceIconController(
+ context: Context,
+ iconView: ImageView
+) : AuthBiometricFingerprintIconController(context, iconView) {
+
+ override val actsAsConfirmButton: Boolean = true
+
+ override fun shouldAnimateForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ): Boolean = when (newState) {
+ STATE_PENDING_CONFIRMATION -> true
+ STATE_AUTHENTICATED -> false
+ else -> super.shouldAnimateForTransition(oldState, newState)
+ }
+
+ override fun getAnimationForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ): Drawable? = when (newState) {
+ STATE_PENDING_CONFIRMATION -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ context.getDrawable(R.drawable.fingerprint_dialog_error_to_unlock)
+ } else {
+ context.getDrawable(R.drawable.fingerprint_dialog_fp_to_unlock)
+ }
+ }
+ STATE_AUTHENTICATED -> null
+ else -> super.getAnimationForTransition(oldState, newState)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
new file mode 100644
index 000000000000..7371442bdd07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.hardware.biometrics.BiometricAuthenticator.Modality
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
+import android.util.AttributeSet
+import com.android.systemui.R
+
+/** Face/Fingerprint combined view for BiometricPrompt. */
+class AuthBiometricFingerprintAndFaceView(
+ context: Context,
+ attrs: AttributeSet?
+) : AuthBiometricFingerprintView(context, attrs) {
+
+ constructor (context: Context) : this(context, null)
+
+ override fun getConfirmationPrompt() = R.string.biometric_dialog_tap_confirm_with_face
+
+ override fun forceRequireConfirmation(@Modality modality: Int) = modality == TYPE_FACE
+
+ override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int) = modality == TYPE_FACE
+
+ override fun onPointerDown(failedModalities: Set<Int>) = failedModalities.contains(TYPE_FACE)
+
+ override fun createIconController(): AuthIconController =
+ AuthBiometricFingerprintAndFaceIconController(mContext, mIconView)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
new file mode 100644
index 000000000000..cd16379cd5b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthBiometricView.BiometricState
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING
+import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN
+import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
+import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
+import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
+import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
+
+/** Fingerprint only icon animator for BiometricPrompt. */
+open class AuthBiometricFingerprintIconController(
+ context: Context,
+ iconView: ImageView
+) : AuthIconController(context, iconView) {
+
+ init {
+ val size = context.resources.getDimensionPixelSize(
+ R.dimen.biometric_dialog_fingerprint_icon_size
+ )
+ iconView.layoutParams.width = size
+ iconView.layoutParams.height = size
+ }
+
+ override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ val icon = getAnimationForTransition(lastState, newState) ?: return
+
+ iconView.setImageDrawable(icon)
+
+ val iconContentDescription = getIconContentDescription(newState)
+ if (iconContentDescription != null) {
+ iconView.contentDescription = iconContentDescription
+ }
+
+ (icon as? AnimatedVectorDrawable)?.apply {
+ reset()
+ if (shouldAnimateForTransition(lastState, newState)) {
+ forceAnimationOnUI()
+ start()
+ }
+ }
+ }
+
+ private fun getIconContentDescription(@BiometricState newState: Int): CharSequence? {
+ val id = when (newState) {
+ STATE_IDLE,
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING,
+ STATE_PENDING_CONFIRMATION,
+ STATE_AUTHENTICATED -> R.string.accessibility_fingerprint_dialog_fingerprint_icon
+ STATE_ERROR,
+ STATE_HELP -> R.string.biometric_dialog_try_again
+ else -> null
+ }
+ return if (id != null) context.getString(id) else null
+ }
+
+ protected open fun shouldAnimateForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ) = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> true
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
+ STATE_AUTHENTICATED -> false
+ else -> false
+ }
+
+ protected open fun getAnimationForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ): Drawable? {
+ val id = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> R.drawable.fingerprint_dialog_fp_to_error
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ R.drawable.fingerprint_dialog_error_to_fp
+ } else {
+ R.drawable.fingerprint_dialog_fp_to_error
+ }
+ }
+ STATE_AUTHENTICATED -> R.drawable.fingerprint_dialog_fp_to_error
+ else -> return null
+ }
+ return if (id != null) context.getDrawable(id) else null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
deleted file mode 100644
index ee602bc9cb78..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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.biometrics;
-
-
-import android.content.Context;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.R;
-
-public class AuthBiometricFingerprintView extends AuthBiometricView {
-
- private static final String TAG = "BiometricPrompt/AuthBiometricFingerprintView";
-
- public AuthBiometricFingerprintView(Context context) {
- this(context, null);
- }
-
- public AuthBiometricFingerprintView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return 0;
- }
-
- @Override
- protected int getStateForAfterError() {
- return STATE_AUTHENTICATING;
- }
-
- @Override
- protected void handleResetAfterError() {
- showTouchSensorString();
- }
-
- @Override
- protected void handleResetAfterHelp() {
- showTouchSensorString();
- }
-
- @Override
- protected boolean supportsSmallDialog() {
- return false;
- }
-
- @Override
- public void updateState(@BiometricState int newState) {
- updateIcon(mState, newState);
-
- // Do this last since the state variable gets updated.
- super.updateState(newState);
- }
-
- @Override
- void onAttachedToWindowInternal() {
- super.onAttachedToWindowInternal();
- showTouchSensorString();
- }
-
- private void showTouchSensorString() {
- mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
- mIndicatorView.setTextColor(mTextColorHint);
- }
-
- private void updateIcon(int lastState, int newState) {
- final Drawable icon = getAnimationForTransition(lastState, newState);
- if (icon == null) {
- Log.e(TAG, "Animation not found, " + lastState + " -> " + newState);
- return;
- }
-
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
-
- mIconView.setImageDrawable(icon);
-
- final CharSequence iconContentDescription = getIconContentDescription(newState);
- if (iconContentDescription != null) {
- mIconView.setContentDescription(iconContentDescription);
- }
-
- if (animation != null && shouldAnimateForTransition(lastState, newState)) {
- animation.forceAnimationOnUI();
- animation.start();
- }
- }
-
- @Nullable
- private CharSequence getIconContentDescription(int newState) {
- switch (newState) {
- case STATE_IDLE:
- case STATE_AUTHENTICATING_ANIMATING_IN:
- case STATE_AUTHENTICATING:
- case STATE_PENDING_CONFIRMATION:
- case STATE_AUTHENTICATED:
- return mContext.getString(
- R.string.accessibility_fingerprint_dialog_fingerprint_icon);
-
- case STATE_ERROR:
- case STATE_HELP:
- return mContext.getString(R.string.biometric_dialog_try_again);
-
- default:
- return null;
- }
- }
-
- private boolean shouldAnimateForTransition(int oldState, int newState) {
- switch (newState) {
- case STATE_HELP:
- case STATE_ERROR:
- return true;
- case STATE_AUTHENTICATING_ANIMATING_IN:
- case STATE_AUTHENTICATING:
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- return true;
- } else {
- return false;
- }
- case STATE_AUTHENTICATED:
- return false;
- default:
- return false;
- }
- }
-
- private Drawable getAnimationForTransition(int oldState, int newState) {
- int iconRes;
-
- switch (newState) {
- case STATE_HELP:
- case STATE_ERROR:
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- break;
- case STATE_AUTHENTICATING_ANIMATING_IN:
- case STATE_AUTHENTICATING:
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- } else {
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- }
- break;
- case STATE_AUTHENTICATED:
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- break;
- default:
- return null;
- }
-
- return mContext.getDrawable(iconRes);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
new file mode 100644
index 000000000000..368bc3aadb70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.biometrics
+
+import android.content.Context
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.util.AttributeSet
+import android.util.Log
+import android.widget.FrameLayout
+import android.widget.TextView
+import com.android.systemui.R
+
+private const val TAG = "AuthBiometricFingerprintView"
+
+/** Fingerprint only view for BiometricPrompt. */
+open class AuthBiometricFingerprintView(
+ context: Context,
+ attrs: AttributeSet? = null
+) : AuthBiometricView(context, attrs) {
+ /** If this view is for a UDFPS sensor. */
+ var isUdfps = false
+ private set
+
+ private var udfpsAdapter: UdfpsDialogMeasureAdapter? = null
+
+ /** Set the [sensorProps] of this sensor so the view can be customized prior to layout. */
+ fun setSensorProperties(sensorProps: FingerprintSensorPropertiesInternal) {
+ isUdfps = sensorProps.isAnyUdfpsType
+ udfpsAdapter = if (isUdfps) UdfpsDialogMeasureAdapter(this, sensorProps) else null
+ }
+
+ override fun onMeasureInternal(width: Int, height: Int): AuthDialog.LayoutParams {
+ val layoutParams = super.onMeasureInternal(width, height)
+ return udfpsAdapter?.onMeasureInternal(width, height, layoutParams) ?: layoutParams
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+
+ val adapter = udfpsAdapter
+ if (adapter != null) {
+ // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen
+ // for devices where the UDFPS sensor is too low.
+ // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap
+ // the button bar area.
+ val bottomSpacerHeight = adapter.bottomSpacerHeight
+ Log.w(TAG, "bottomSpacerHeight: $bottomSpacerHeight")
+ if (bottomSpacerHeight < 0) {
+ val iconFrame = findViewById<FrameLayout>(R.id.biometric_icon_frame)!!
+ iconFrame.translationY = -bottomSpacerHeight.toFloat()
+ val indicator = findViewById<TextView>(R.id.indicator)!!
+ indicator.translationY = -bottomSpacerHeight.toFloat()
+ }
+ }
+ }
+
+ override fun getDelayAfterAuthenticatedDurationMs() = 0
+
+ override fun getStateForAfterError() = STATE_AUTHENTICATING
+
+ override fun handleResetAfterError() = showTouchSensorString()
+
+ override fun handleResetAfterHelp() = showTouchSensorString()
+
+ override fun supportsSmallDialog() = false
+
+ override fun createIconController(): AuthIconController =
+ AuthBiometricFingerprintIconController(mContext, mIconView)
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ showTouchSensorString()
+ }
+
+ private fun showTouchSensorString() {
+ mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor)
+ mIndicatorView.setTextColor(mTextColorHint)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
new file mode 100644
index 000000000000..ce5e600e6a77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.annotation.DrawableRes
+import android.content.Context
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import android.widget.ImageView
+import com.android.systemui.biometrics.AuthBiometricView.BiometricState
+
+private const val TAG = "AuthIconController"
+
+/** Controller for animating the BiometricPrompt icon/affordance. */
+abstract class AuthIconController(
+ protected val context: Context,
+ protected val iconView: ImageView
+) : Animatable2.AnimationCallback() {
+
+ /** If this controller should ignore events and pause. */
+ var deactivated: Boolean = false
+
+ /** If the icon view should be treated as an alternate "confirm" button. */
+ open val actsAsConfirmButton: Boolean = false
+
+ final override fun onAnimationStart(drawable: Drawable) {
+ super.onAnimationStart(drawable)
+ }
+
+ final override fun onAnimationEnd(drawable: Drawable) {
+ super.onAnimationEnd(drawable)
+
+ if (!deactivated) {
+ handleAnimationEnd(drawable)
+ }
+ }
+
+ /** Set the icon to a static image. */
+ protected fun showStaticDrawable(@DrawableRes iconRes: Int) {
+ iconView.setImageDrawable(context.getDrawable(iconRes))
+ }
+
+ /** Animate a resource. */
+ protected fun animateIconOnce(@DrawableRes iconRes: Int) {
+ animateIcon(iconRes, false)
+ }
+
+ /** Animate a resource. */
+ protected fun animateIcon(@DrawableRes iconRes: Int, repeat: Boolean) {
+ if (!deactivated) {
+ val icon = context.getDrawable(iconRes) as AnimatedVectorDrawable
+ iconView.setImageDrawable(icon)
+ icon.forceAnimationOnUI()
+ if (repeat) {
+ icon.registerAnimationCallback(this)
+ }
+ icon.start()
+ }
+ }
+
+ /** Update the icon to reflect the [newState]. */
+ fun updateState(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ if (deactivated) {
+ Log.w(TAG, "Ignoring updateState when deactivated: $newState")
+ } else {
+ updateIcon(lastState, newState)
+ }
+ }
+
+ /** If the icon should act as a "retry" button in the [currentState]. */
+ fun iconTapSendsRetryWhen(@BiometricState currentState: Int): Boolean = false
+
+ /** Call during [updateState] if the controller is not [deactivated]. */
+ abstract fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int)
+
+ /** Called during [onAnimationEnd] if the controller is not [deactivated]. */
+ open fun handleAnimationEnd(drawable: Drawable) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
deleted file mode 100644
index d80d9cc9d62d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-/**
- * Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements
- * do not overlap with
- */
-public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
- private static final String TAG = "AuthBiometricUdfpsView";
-
- @Nullable private UdfpsDialogMeasureAdapter mMeasureAdapter;
-
- public AuthBiometricUdfpsView(Context context) {
- this(context, null /* attrs */);
- }
-
- public AuthBiometricUdfpsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- void setSensorProps(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
- if (mMeasureAdapter == null || mMeasureAdapter.getSensorProps() != sensorProps) {
- mMeasureAdapter = new UdfpsDialogMeasureAdapter(this, sensorProps);
- }
- }
-
- @Override
- @NonNull
- AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
- final AuthDialog.LayoutParams layoutParams = super.onMeasureInternal(width, height);
- return mMeasureAdapter != null
- ? mMeasureAdapter.onMeasureInternal(width, height, layoutParams)
- : layoutParams;
- }
-
- @Override
- void onLayoutInternal() {
- super.onLayoutInternal();
-
- // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen
- // for devices where the UDFPS sensor is too low.
- // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap
- // the button bar area.
- final int bottomSpacerHeight = mMeasureAdapter.getBottomSpacerHeight();
- Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
- if (bottomSpacerHeight < 0) {
- FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
- iconFrame.setTranslationY(-bottomSpacerHeight);
-
- TextView indicator = findViewById(R.id.indicator);
- indicator.setTranslationY(-bottomSpacerHeight);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 1496f170dffe..76d4aa839ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -25,6 +25,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricPrompt;
@@ -44,19 +45,21 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
- * Contains the Biometric views (title, subtitle, icon, buttons, etc) and its controllers.
+ * Contains the Biometric views (title, subtitle, icon, buttons, etc.) and its controllers.
*/
-public abstract class AuthBiometricView extends LinearLayout {
+public class AuthBiometricView extends LinearLayout {
- private static final String TAG = "BiometricPrompt/AuthBiometricView";
+ private static final String TAG = "AuthBiometricView";
/**
* Authentication hardware idle.
@@ -102,13 +105,6 @@ public abstract class AuthBiometricView extends LinearLayout {
int ACTION_BUTTON_TRY_AGAIN = 4;
int ACTION_ERROR = 5;
int ACTION_USE_DEVICE_CREDENTIAL = 6;
- /**
- * Notify the receiver to start the fingerprint sensor.
- *
- * This is only applicable to multi-sensor devices that need to delay fingerprint auth
- * (i.e face -> fingerprint).
- */
- int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -118,66 +114,9 @@ public abstract class AuthBiometricView extends LinearLayout {
void onAction(int action);
}
- @VisibleForTesting
- static class Injector {
- AuthBiometricView mBiometricView;
-
- public Button getNegativeButton() {
- return mBiometricView.findViewById(R.id.button_negative);
- }
-
- public Button getCancelButton() {
- return mBiometricView.findViewById(R.id.button_cancel);
- }
-
- public Button getUseCredentialButton() {
- return mBiometricView.findViewById(R.id.button_use_credential);
- }
-
- public Button getConfirmButton() {
- return mBiometricView.findViewById(R.id.button_confirm);
- }
-
- public Button getTryAgainButton() {
- return mBiometricView.findViewById(R.id.button_try_again);
- }
-
- public TextView getTitleView() {
- return mBiometricView.findViewById(R.id.title);
- }
-
- public TextView getSubtitleView() {
- return mBiometricView.findViewById(R.id.subtitle);
- }
-
- public TextView getDescriptionView() {
- return mBiometricView.findViewById(R.id.description);
- }
-
- public TextView getIndicatorView() {
- return mBiometricView.findViewById(R.id.indicator);
- }
-
- public ImageView getIconView() {
- return mBiometricView.findViewById(R.id.biometric_icon);
- }
-
- public View getIconHolderView() {
- return mBiometricView.findViewById(R.id.biometric_icon_frame);
- }
-
- public int getDelayAfterError() {
- return BiometricPrompt.HIDE_DIALOG_DELAY;
- }
-
- public int getMediumToLargeAnimationDurationMs() {
- return AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS;
- }
- }
-
- private final Injector mInjector;
protected final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
+ private final LockPatternUtils mLockPatternUtils;
protected final int mTextColorError;
protected final int mTextColorHint;
@@ -195,6 +134,11 @@ public abstract class AuthBiometricView extends LinearLayout {
protected ImageView mIconView;
protected TextView mIndicatorView;
+ @VisibleForTesting @NonNull AuthIconController mIconController;
+ @VisibleForTesting int mAnimationDurationShort = AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS;
+ @VisibleForTesting int mAnimationDurationLong = AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS;
+ @VisibleForTesting int mAnimationDurationHideDialog = BiometricPrompt.HIDE_DIALOG_DELAY;
+
// Negative button position, exclusively for the app-specified behavior
@VisibleForTesting Button mNegativeButton;
// Negative button position, exclusively for cancelling auth after passive auth success
@@ -217,30 +161,7 @@ public abstract class AuthBiometricView extends LinearLayout {
protected boolean mDialogSizeAnimating;
protected Bundle mSavedState;
- /**
- * Delay after authentication is confirmed, before the dialog should be animated away.
- */
- protected abstract int getDelayAfterAuthenticatedDurationMs();
- /**
- * State that the dialog/icon should be in after showing a help message.
- */
- protected abstract int getStateForAfterError();
- /**
- * Invoked when the error message is being cleared.
- */
- protected abstract void handleResetAfterError();
- /**
- * Invoked when the help message is being cleared.
- */
- protected abstract void handleResetAfterHelp();
-
- /**
- * @return true if the dialog supports {@link AuthDialog.DialogSize#SIZE_SMALL}
- */
- protected abstract boolean supportsSmallDialog();
-
private final Runnable mResetErrorRunnable;
-
private final Runnable mResetHelpRunnable;
private final OnClickListener mBackgroundClickListener = (view) -> {
@@ -262,11 +183,6 @@ public abstract class AuthBiometricView extends LinearLayout {
}
public AuthBiometricView(Context context, AttributeSet attrs) {
- this(context, attrs, new Injector());
- }
-
- @VisibleForTesting
- AuthBiometricView(Context context, AttributeSet attrs, Injector injector) {
super(context, attrs);
mHandler = new Handler(Looper.getMainLooper());
mTextColorError = getResources().getColor(
@@ -274,10 +190,8 @@ public abstract class AuthBiometricView extends LinearLayout {
mTextColorHint = getResources().getColor(
R.color.biometric_dialog_gray, context.getTheme());
- mInjector = injector;
- mInjector.mBiometricView = this;
-
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mLockPatternUtils = new LockPatternUtils(context);
mResetErrorRunnable = () -> {
updateState(getStateForAfterError());
@@ -292,36 +206,91 @@ public abstract class AuthBiometricView extends LinearLayout {
};
}
- public void setPanelController(AuthPanelController panelController) {
+ /** Delay after authentication is confirmed, before the dialog should be animated away. */
+ protected int getDelayAfterAuthenticatedDurationMs() {
+ return 0;
+ }
+
+ /** State that the dialog/icon should be in after showing a help message. */
+ protected int getStateForAfterError() {
+ return STATE_IDLE;
+ }
+
+ /** Invoked when the error message is being cleared. */
+ protected void handleResetAfterError() {}
+
+ /** Invoked when the help message is being cleared. */
+ protected void handleResetAfterHelp() {}
+
+ /** True if the dialog supports {@link AuthDialog.DialogSize#SIZE_SMALL}. */
+ protected boolean supportsSmallDialog() {
+ return false;
+ }
+
+ /** The string to show when the user must tap to confirm via the button or icon. */
+ @StringRes
+ protected int getConfirmationPrompt() {
+ return R.string.biometric_dialog_tap_confirm;
+ }
+
+ /** True if require confirmation will be honored when set via the API. */
+ protected boolean supportsRequireConfirmation() {
+ return false;
+ }
+
+ /** True if confirmation will be required even if it was not supported/requested. */
+ protected boolean forceRequireConfirmation(@Modality int modality) {
+ return false;
+ }
+
+ /** Ignore all events from this (secondary) modality except successful authentication. */
+ protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality) {
+ return false;
+ }
+
+ /**
+ * Create the controller for managing the icons transitions during the prompt.
+ *
+ * Subclass should override.
+ */
+ @NonNull
+ protected AuthIconController createIconController() {
+ return new AuthIconController(mContext, mIconView) {
+ @Override
+ public void updateIcon(int lastState, int newState) {}
+ };
+ }
+
+ void setPanelController(AuthPanelController panelController) {
mPanelController = panelController;
}
- public void setPromptInfo(PromptInfo promptInfo) {
+ void setPromptInfo(PromptInfo promptInfo) {
mPromptInfo = promptInfo;
}
- public void setCallback(Callback callback) {
+ void setCallback(Callback callback) {
mCallback = callback;
}
- public void setBackgroundView(View backgroundView) {
+ void setBackgroundView(View backgroundView) {
backgroundView.setOnClickListener(mBackgroundClickListener);
}
- public void setUserId(int userId) {
+ void setUserId(int userId) {
mUserId = userId;
}
- public void setEffectiveUserId(int effectiveUserId) {
+ void setEffectiveUserId(int effectiveUserId) {
mEffectiveUserId = effectiveUserId;
}
- public void setRequireConfirmation(boolean requireConfirmation) {
- mRequireConfirmation = requireConfirmation;
+ void setRequireConfirmation(boolean requireConfirmation) {
+ mRequireConfirmation = requireConfirmation && supportsRequireConfirmation();
}
@VisibleForTesting
- void updateSize(@AuthDialog.DialogSize int newSize) {
+ final void updateSize(@AuthDialog.DialogSize int newSize) {
Log.v(TAG, "Current size: " + mSize + " New size: " + newSize);
if (newSize == AuthDialog.SIZE_SMALL) {
mTitleView.setVisibility(View.GONE);
@@ -376,7 +345,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// Choreograph together
final AnimatorSet as = new AnimatorSet();
- as.setDuration(AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS);
+ as.setDuration(mAnimationDurationShort);
as.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -429,7 +398,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// Translate at full duration
final ValueAnimator translationAnimator = ValueAnimator.ofFloat(
biometricView.getY(), biometricView.getY() - translationY);
- translationAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs());
+ translationAnimator.setDuration(mAnimationDurationLong);
translationAnimator.addUpdateListener((animation) -> {
final float translation = (float) animation.getAnimatedValue();
biometricView.setTranslationY(translation);
@@ -438,7 +407,7 @@ public abstract class AuthBiometricView extends LinearLayout {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- if (biometricView.getParent() != null) {
+ if (biometricView.getParent() instanceof ViewGroup) {
((ViewGroup) biometricView.getParent()).removeView(biometricView);
}
mSize = newSize;
@@ -447,7 +416,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// Opacity to 0 in half duration
final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(1, 0);
- opacityAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs() / 2);
+ opacityAnimator.setDuration(mAnimationDurationLong / 2);
opacityAnimator.addUpdateListener((animation) -> {
final float opacity = (float) animation.getAnimatedValue();
biometricView.setAlpha(opacity);
@@ -457,7 +426,7 @@ public abstract class AuthBiometricView extends LinearLayout {
mPanelController.updateForContentDimensions(
mPanelController.getContainerWidth(),
mPanelController.getContainerHeight(),
- mInjector.getMediumToLargeAnimationDurationMs());
+ mAnimationDurationLong);
// Start the animations together
AnimatorSet as = new AnimatorSet();
@@ -466,7 +435,7 @@ public abstract class AuthBiometricView extends LinearLayout {
animators.add(opacityAnimator);
as.playTogether(animators);
- as.setDuration(mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3);
+ as.setDuration(mAnimationDurationLong * 2 / 3);
as.start();
} else {
Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize);
@@ -481,6 +450,8 @@ public abstract class AuthBiometricView extends LinearLayout {
public void updateState(@BiometricState int newState) {
Log.v(TAG, "newState: " + newState);
+ mIconController.updateState(mState, newState);
+
switch (newState) {
case STATE_AUTHENTICATING_ANIMATING_IN:
case STATE_AUTHENTICATING:
@@ -510,10 +481,11 @@ public abstract class AuthBiometricView extends LinearLayout {
mNegativeButton.setVisibility(View.GONE);
mCancelButton.setVisibility(View.VISIBLE);
mUseCredentialButton.setVisibility(View.GONE);
- mConfirmButton.setEnabled(true);
- mConfirmButton.setVisibility(View.VISIBLE);
+ // forced confirmations (multi-sensor) use the icon view as the confirm button
+ mConfirmButton.setEnabled(mRequireConfirmation);
+ mConfirmButton.setVisibility(mRequireConfirmation ? View.VISIBLE : View.GONE);
mIndicatorView.setTextColor(mTextColorHint);
- mIndicatorView.setText(R.string.biometric_dialog_tap_confirm);
+ mIndicatorView.setText(getConfirmationPrompt());
mIndicatorView.setVisibility(View.VISIBLE);
break;
@@ -536,9 +508,9 @@ public abstract class AuthBiometricView extends LinearLayout {
updateState(STATE_AUTHENTICATING);
}
- public void onAuthenticationSucceeded() {
+ public void onAuthenticationSucceeded(@Modality int modality) {
removePendingAnimations();
- if (mRequireConfirmation) {
+ if (mRequireConfirmation || forceRequireConfirmation(modality)) {
updateState(STATE_PENDING_CONFIRMATION);
} else {
updateState(STATE_AUTHENTICATED);
@@ -553,6 +525,10 @@ public abstract class AuthBiometricView extends LinearLayout {
*/
public void onAuthenticationFailed(
@Modality int modality, @Nullable String failureReason) {
+ if (ignoreUnsuccessfulEventsFrom(modality)) {
+ return;
+ }
+
showTemporaryMessage(failureReason, mResetErrorRunnable);
updateState(STATE_ERROR);
}
@@ -564,12 +540,27 @@ public abstract class AuthBiometricView extends LinearLayout {
* @param error message
*/
public void onError(@Modality int modality, String error) {
+ if (ignoreUnsuccessfulEventsFrom(modality)) {
+ return;
+ }
+
showTemporaryMessage(error, mResetErrorRunnable);
updateState(STATE_ERROR);
- mHandler.postDelayed(() -> {
- mCallback.onAction(Callback.ACTION_ERROR);
- }, mInjector.getDelayAfterError());
+ mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_ERROR),
+ mAnimationDurationHideDialog);
+ }
+
+ /**
+ * Fingerprint pointer down event. This does nothing by default and will not be called if the
+ * device does not have an appropriate sensor (UDFPS), but it may be used as an alternative
+ * to the "retry" button when fingerprint is used with other modalities.
+ *
+ * @param failedModalities the set of modalities that have failed
+ * @return true if a retry was initiated as a result of this event
+ */
+ public boolean onPointerDown(Set<Integer> failedModalities) {
+ return false;
}
/**
@@ -579,6 +570,9 @@ public abstract class AuthBiometricView extends LinearLayout {
* @param help message
*/
public void onHelp(@Modality int modality, String help) {
+ if (ignoreUnsuccessfulEventsFrom(modality)) {
+ return;
+ }
if (mSize != AuthDialog.SIZE_MEDIUM) {
Log.w(TAG, "Help received in size: " + mSize);
return;
@@ -639,7 +633,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// select to enable marquee unless a screen reader is enabled
mIndicatorView.setSelected(!mAccessibilityManager.isEnabled()
|| !mAccessibilityManager.isTouchExplorationEnabled());
- mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
+ mHandler.postDelayed(resetMessageRunnable, mAnimationDurationHideDialog);
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
@@ -647,29 +641,22 @@ public abstract class AuthBiometricView extends LinearLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- onFinishInflateInternal();
- }
- /**
- * After inflation, but before things like restoreState, onAttachedToWindow, etc.
- */
- @VisibleForTesting
- void onFinishInflateInternal() {
- mTitleView = mInjector.getTitleView();
- mSubtitleView = mInjector.getSubtitleView();
- mDescriptionView = mInjector.getDescriptionView();
- mIconView = mInjector.getIconView();
- mIconHolderView = mInjector.getIconHolderView();
- mIndicatorView = mInjector.getIndicatorView();
+ mTitleView = findViewById(R.id.title);
+ mSubtitleView = findViewById(R.id.subtitle);
+ mDescriptionView = findViewById(R.id.description);
+ mIconView = findViewById(R.id.biometric_icon);
+ mIconHolderView = findViewById(R.id.biometric_icon_frame);
+ mIndicatorView = findViewById(R.id.indicator);
// Negative-side (left) buttons
- mNegativeButton = mInjector.getNegativeButton();
- mCancelButton = mInjector.getCancelButton();
- mUseCredentialButton = mInjector.getUseCredentialButton();
+ mNegativeButton = findViewById(R.id.button_negative);
+ mCancelButton = findViewById(R.id.button_cancel);
+ mUseCredentialButton = findViewById(R.id.button_use_credential);
// Positive-side (right) buttons
- mConfirmButton = mInjector.getConfirmButton();
- mTryAgainButton = mInjector.getTryAgainButton();
+ mConfirmButton = findViewById(R.id.button_confirm);
+ mTryAgainButton = findViewById(R.id.button_try_again);
mNegativeButton.setOnClickListener((view) -> {
mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
@@ -693,6 +680,15 @@ public abstract class AuthBiometricView extends LinearLayout {
mTryAgainButton.setVisibility(View.GONE);
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
});
+
+ mIconController = createIconController();
+ if (mIconController.getActsAsConfirmButton()) {
+ mIconView.setOnClickListener((view) -> {
+ if (mState == STATE_PENDING_CONFIRMATION) {
+ updateState(STATE_AUTHENTICATED);
+ }
+ });
+ }
}
/**
@@ -706,21 +702,13 @@ public abstract class AuthBiometricView extends LinearLayout {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- onAttachedToWindowInternal();
- }
- /**
- * Contains all the testable logic that should be invoked when {@link #onAttachedToWindow()} is
- * invoked.
- */
- @VisibleForTesting
- void onAttachedToWindowInternal() {
mTitleView.setText(mPromptInfo.getTitle());
if (isDeviceCredentialAllowed()) {
final CharSequence credentialButtonText;
- final @Utils.CredentialType int credentialType =
- Utils.getCredentialType(mContext, mEffectiveUserId);
+ @Utils.CredentialType final int credentialType =
+ Utils.getCredentialType(mLockPatternUtils, mEffectiveUserId);
switch (credentialType) {
case Utils.CREDENTIAL_PIN:
credentialButtonText =
@@ -731,9 +719,6 @@ public abstract class AuthBiometricView extends LinearLayout {
getResources().getString(R.string.biometric_dialog_use_pattern);
break;
case Utils.CREDENTIAL_PASSWORD:
- credentialButtonText =
- getResources().getString(R.string.biometric_dialog_use_password);
- break;
default:
credentialButtonText =
getResources().getString(R.string.biometric_dialog_use_password);
@@ -749,7 +734,6 @@ public abstract class AuthBiometricView extends LinearLayout {
}
setTextOrHide(mSubtitleView, mPromptInfo.getSubtitle());
-
setTextOrHide(mDescriptionView, mPromptInfo.getDescription());
if (mSavedState == null) {
@@ -774,6 +758,8 @@ public abstract class AuthBiometricView extends LinearLayout {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ mIconController.setDeactivated(true);
+
// Empty the handler, otherwise things like ACTION_AUTHENTICATED may be duplicated once
// the new dialog is restored.
mHandler.removeCallbacksAndMessages(null /* all */);
@@ -856,15 +842,7 @@ public abstract class AuthBiometricView extends LinearLayout {
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- onLayoutInternal();
- }
- /**
- * Contains all the testable logic that should be invoked when
- * {@link #onLayout(boolean, int, int, int, int)}, is invoked.
- */
- @VisibleForTesting
- void onLayoutInternal() {
// Start with initial size only once. Subsequent layout changes don't matter since we
// only care about the initial icon position.
if (mIconOriginalY == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 21edb2478c79..6b6af4c7b52f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -16,9 +16,10 @@
package com.android.systemui.biometrics;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
+import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,13 +53,16 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Top level container/controller for the BiometricPrompt UI.
@@ -66,54 +70,52 @@ import java.util.List;
public class AuthContainerView extends LinearLayout
implements AuthDialog, WakefulnessLifecycle.Observer {
- private static final String TAG = "BiometricPrompt/AuthContainerView";
+ private static final String TAG = "AuthContainerView";
+
private static final int ANIMATION_DURATION_SHOW_MS = 250;
- private static final int ANIMATION_DURATION_AWAY_MS = 350; // ms
+ private static final int ANIMATION_DURATION_AWAY_MS = 350;
- static final int STATE_UNKNOWN = 0;
- static final int STATE_ANIMATING_IN = 1;
- static final int STATE_PENDING_DISMISS = 2;
- static final int STATE_SHOWING = 3;
- static final int STATE_ANIMATING_OUT = 4;
- static final int STATE_GONE = 5;
+ private static final int STATE_UNKNOWN = 0;
+ private static final int STATE_ANIMATING_IN = 1;
+ private static final int STATE_PENDING_DISMISS = 2;
+ private static final int STATE_SHOWING = 3;
+ private static final int STATE_ANIMATING_OUT = 4;
+ private static final int STATE_GONE = 5;
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_UNKNOWN, STATE_ANIMATING_IN, STATE_PENDING_DISMISS, STATE_SHOWING,
STATE_ANIMATING_OUT, STATE_GONE})
- @interface ContainerState {}
+ private @interface ContainerState {}
- final Config mConfig;
- final int mEffectiveUserId;
- @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps;
- @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
+ private final Config mConfig;
+ private final int mEffectiveUserId;
private final Handler mHandler;
- private final Injector mInjector;
private final IBinder mWindowToken = new Binder();
private final WindowManager mWindowManager;
- private final AuthPanelController mPanelController;
private final Interpolator mLinearOutSlowIn;
- @VisibleForTesting final BiometricCallback mBiometricCallback;
private final CredentialCallback mCredentialCallback;
+ private final LockPatternUtils mLockPatternUtils;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
- @VisibleForTesting final FrameLayout mFrameLayout;
- @VisibleForTesting @Nullable AuthBiometricView mBiometricView;
- @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
+ @VisibleForTesting final BiometricCallback mBiometricCallback;
- @VisibleForTesting final ImageView mBackgroundView;
- @VisibleForTesting final ScrollView mBiometricScrollView;
+ @Nullable private AuthBiometricView mBiometricView;
+ @Nullable private AuthCredentialView mCredentialView;
+ private final AuthPanelController mPanelController;
+ private final FrameLayout mFrameLayout;
+ private final ImageView mBackgroundView;
+ private final ScrollView mBiometricScrollView;
private final View mPanelView;
-
private final float mTranslationY;
-
- private final WakefulnessLifecycle mWakefulnessLifecycle;
-
- @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
+ @ContainerState private int mContainerState = STATE_UNKNOWN;
+ private final Set<Integer> mFailedModalities = new HashSet<Integer>();
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
- @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
+ @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
// HAT received from LockSettingsService when credential is verified.
- @Nullable byte[] mCredentialAttestation;
+ @Nullable private byte[] mCredentialAttestation;
+ @VisibleForTesting
static class Config {
Context mContext;
AuthDialogCallback mCallback;
@@ -122,11 +124,11 @@ public class AuthContainerView extends LinearLayout
int mUserId;
String mOpPackageName;
int[] mSensorIds;
- boolean mCredentialAllowed;
boolean mSkipIntro;
long mOperationId;
long mRequestId;
- @BiometricMultiSensorMode int mMultiSensorConfig;
+ boolean mSkipAnimation = false;
+ @BiometricMultiSensorMode int mMultiSensorConfig = BIOMETRIC_MULTI_SENSOR_DEFAULT;
}
public static class Builder {
@@ -167,7 +169,7 @@ public class AuthContainerView extends LinearLayout
return this;
}
- public Builder setOperationId(long operationId) {
+ public Builder setOperationId(@DurationMillisLong long operationId) {
mConfig.mOperationId = operationId;
return this;
}
@@ -178,55 +180,27 @@ public class AuthContainerView extends LinearLayout
return this;
}
+ @VisibleForTesting
+ public Builder setSkipAnimationDuration(boolean skip) {
+ mConfig.mSkipAnimation = skip;
+ return this;
+ }
+
/** The multi-sensor mode. */
public Builder setMultiSensorConfig(@BiometricMultiSensorMode int multiSensorConfig) {
mConfig.mMultiSensorConfig = multiSensorConfig;
return this;
}
- public AuthContainerView build(int[] sensorIds, boolean credentialAllowed,
+ public AuthContainerView build(int[] sensorIds,
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ @NonNull WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull UserManager userManager,
+ @NonNull LockPatternUtils lockPatternUtils) {
mConfig.mSensorIds = sensorIds;
- mConfig.mCredentialAllowed = credentialAllowed;
- return new AuthContainerView(
- mConfig, new Injector(), fpProps, faceProps, wakefulnessLifecycle);
- }
- }
-
- public static class Injector {
- ScrollView getBiometricScrollView(FrameLayout parent) {
- return parent.findViewById(R.id.biometric_scrollview);
- }
-
- FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
- return (FrameLayout) factory.inflate(
- R.layout.auth_container_view, root, false /* attachToRoot */);
- }
-
- AuthPanelController getPanelController(Context context, View panelView) {
- return new AuthPanelController(context, panelView);
- }
-
- ImageView getBackgroundView(FrameLayout parent) {
- return parent.findViewById(R.id.background);
- }
-
- View getPanelView(FrameLayout parent) {
- return parent.findViewById(R.id.panel);
- }
-
- int getAnimateCredentialStartDelayMs() {
- return AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS;
- }
-
- UserManager getUserManager(Context context) {
- return UserManager.get(context);
- }
-
- int getCredentialType(Context context, int effectiveUserId) {
- return Utils.getCredentialType(context, effectiveUserId);
+ return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
+ userManager, lockPatternUtils, new Handler(Looper.getMainLooper()));
}
}
@@ -246,6 +220,7 @@ public class AuthContainerView extends LinearLayout
animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
break;
case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN:
+ mFailedModalities.clear();
mConfig.mCallback.onTryAgainPressed();
break;
case AuthBiometricView.Callback.ACTION_ERROR:
@@ -255,10 +230,7 @@ public class AuthContainerView extends LinearLayout
mConfig.mCallback.onDeviceCredentialPressed();
mHandler.postDelayed(() -> {
addCredentialView(false /* animatePanel */, true /* animateContents */);
- }, mInjector.getAnimateCredentialStartDelayMs());
- break;
- case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR:
- mConfig.mCallback.onStartFingerprintNow();
+ }, mConfig.mSkipAnimation ? 0 : AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS);
break;
default:
Log.e(TAG, "Unhandled action: " + action);
@@ -275,21 +247,19 @@ public class AuthContainerView extends LinearLayout
}
@VisibleForTesting
- AuthContainerView(Config config, Injector injector,
+ AuthContainerView(Config config,
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ @NonNull WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull UserManager userManager,
+ @NonNull LockPatternUtils lockPatternUtils,
+ @NonNull Handler mainHandler) {
super(config.mContext);
mConfig = config;
- mInjector = injector;
- mFpProps = fpProps;
- mFaceProps = faceProps;
-
- mEffectiveUserId = mInjector.getUserManager(mContext)
- .getCredentialOwnerProfile(mConfig.mUserId);
-
- mHandler = new Handler(Looper.getMainLooper());
+ mLockPatternUtils = lockPatternUtils;
+ mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId);
+ mHandler = mainHandler;
mWindowManager = mContext.getSystemService(WindowManager.class);
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -299,100 +269,42 @@ public class AuthContainerView extends LinearLayout
mBiometricCallback = new BiometricCallback();
mCredentialCallback = new CredentialCallback();
- final LayoutInflater factory = LayoutInflater.from(mContext);
- mFrameLayout = mInjector.inflateContainerView(factory, this);
-
- mPanelView = mInjector.getPanelView(mFrameLayout);
- mPanelController = mInjector.getPanelController(mContext, mPanelView);
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ mFrameLayout = (FrameLayout) layoutInflater.inflate(
+ R.layout.auth_container_view, this, false /* attachToRoot */);
+ addView(mFrameLayout);
+ mBiometricScrollView = mFrameLayout.findViewById(R.id.biometric_scrollview);
+ mBackgroundView = mFrameLayout.findViewById(R.id.background);
+ mPanelView = mFrameLayout.findViewById(R.id.panel);
+ mPanelController = new AuthPanelController(mContext, mPanelView);
// Inflate biometric view only if necessary.
- final int sensorCount = config.mSensorIds.length;
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
- if (sensorCount == 1) {
- final int singleSensorAuthId = config.mSensorIds[0];
- if (Utils.containsSensorId(mFpProps, singleSensorAuthId)) {
- FingerprintSensorPropertiesInternal sensorProps = null;
- for (FingerprintSensorPropertiesInternal prop : mFpProps) {
- if (prop.sensorId == singleSensorAuthId) {
- sensorProps = prop;
- break;
- }
- }
-
- if (sensorProps.isAnyUdfpsType()) {
- AuthBiometricUdfpsView udfpsView = (AuthBiometricUdfpsView) factory
- .inflate(R.layout.auth_biometric_udfps_view, null, false);
- udfpsView.setSensorProps(sensorProps);
- mBiometricView = udfpsView;
- } else {
- mBiometricView = (AuthBiometricFingerprintView) factory
- .inflate(R.layout.auth_biometric_fingerprint_view, null, false);
- }
- } else if (Utils.containsSensorId(mFaceProps, singleSensorAuthId)) {
- mBiometricView = (AuthBiometricFaceView)
- factory.inflate(R.layout.auth_biometric_face_view, null, false);
- } else {
- // Unknown sensorId
- Log.e(TAG, "Unknown sensorId: " + singleSensorAuthId);
- mBiometricView = null;
- mBackgroundView = null;
- mBiometricScrollView = null;
- return;
- }
- } else if (sensorCount == 2) {
- final int[] allSensors = findFaceAndFingerprintSensors();
- final int faceSensorId = allSensors[0];
- final int fingerprintSensorId = allSensors[1];
-
- if (fingerprintSensorId == -1 || faceSensorId == -1) {
- Log.e(TAG, "Missing fingerprint or face for dual-sensor config");
- mBiometricView = null;
- mBackgroundView = null;
- mBiometricScrollView = null;
- return;
- }
-
- FingerprintSensorPropertiesInternal fingerprintSensorProps = null;
- for (FingerprintSensorPropertiesInternal prop : mFpProps) {
- if (prop.sensorId == fingerprintSensorId) {
- fingerprintSensorProps = prop;
- break;
- }
- }
-
- if (fingerprintSensorProps != null) {
- final AuthBiometricFaceToFingerprintView faceToFingerprintView =
- (AuthBiometricFaceToFingerprintView) factory.inflate(
- R.layout.auth_biometric_face_to_fingerprint_view, null, false);
- faceToFingerprintView.setFingerprintSensorProps(fingerprintSensorProps);
- faceToFingerprintView.setModalityListener(new ModalityListener() {
- @Override
- public void onModalitySwitched(int oldModality, int newModality) {
- maybeUpdatePositionForUdfps(true /* invalidate */);
- }
- });
- mBiometricView = faceToFingerprintView;
- } else {
- Log.e(TAG, "Fingerprint props not found for sensor ID: " + fingerprintSensorId);
- mBiometricView = null;
- mBackgroundView = null;
- mBiometricScrollView = null;
- return;
- }
+ final FingerprintSensorPropertiesInternal fpProperties =
+ Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds);
+ final FaceSensorPropertiesInternal faceProperties =
+ Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds);
+
+ if (fpProperties != null && faceProperties != null) {
+ final AuthBiometricFingerprintAndFaceView fingerprintAndFaceView =
+ (AuthBiometricFingerprintAndFaceView) layoutInflater.inflate(
+ R.layout.auth_biometric_fingerprint_and_face_view, null, false);
+ fingerprintAndFaceView.setSensorProperties(fpProperties);
+ mBiometricView = fingerprintAndFaceView;
+ } else if (fpProperties != null) {
+ final AuthBiometricFingerprintView fpView =
+ (AuthBiometricFingerprintView) layoutInflater.inflate(
+ R.layout.auth_biometric_fingerprint_view, null, false);
+ fpView.setSensorProperties(fpProperties);
+ mBiometricView = fpView;
+ } else if (faceProperties != null) {
+ mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate(
+ R.layout.auth_biometric_face_view, null, false);
} else {
- Log.e(TAG, "Unsupported sensor array, length: " + sensorCount);
- mBiometricView = null;
- mBackgroundView = null;
- mBiometricScrollView = null;
- return;
+ Log.e(TAG, "No sensors found!");
}
}
- mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout);
- mBackgroundView = mInjector.getBackgroundView(mFrameLayout);
-
- addView(mFrameLayout);
-
// init view before showing
if (mBiometricView != null) {
mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
@@ -431,10 +343,6 @@ public class AuthContainerView extends LinearLayout
return Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo);
}
- private void addBiometricView() {
- mBiometricScrollView.addView(mBiometricView);
- }
-
/**
* Adds the credential view. When going from biometric to credential view, the biometric
* view starts the panel expansion animation. If the credential view is being shown first,
@@ -444,8 +352,8 @@ public class AuthContainerView extends LinearLayout
private void addCredentialView(boolean animatePanel, boolean animateContents) {
final LayoutInflater factory = LayoutInflater.from(mContext);
- final @Utils.CredentialType int credentialType = mInjector.getCredentialType(
- mContext, mEffectiveUserId);
+ @Utils.CredentialType final int credentialType = Utils.getCredentialType(
+ mLockPatternUtils, mEffectiveUserId);
switch (credentialType) {
case Utils.CREDENTIAL_PATTERN:
@@ -493,15 +401,11 @@ public class AuthContainerView extends LinearLayout
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- onAttachedToWindowInternal();
- }
- @VisibleForTesting
- void onAttachedToWindowInternal() {
mWakefulnessLifecycle.addObserver(this);
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
- addBiometricView();
+ mBiometricScrollView.addView(mBiometricView);
} else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true /* animatePanel */, false /* animateContents */);
} else {
@@ -521,17 +425,18 @@ public class AuthContainerView extends LinearLayout
mBiometricScrollView.setY(mTranslationY);
setAlpha(0f);
+ final long animateDuration = mConfig.mSkipAnimation ? 0 : ANIMATION_DURATION_SHOW_MS;
postOnAnimation(() -> {
mPanelView.animate()
.translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.withEndAction(this::onDialogAnimatedIn)
.start();
mBiometricScrollView.animate()
.translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
@@ -539,14 +444,14 @@ public class AuthContainerView extends LinearLayout
mCredentialView.setY(mTranslationY);
mCredentialView.animate()
.translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
}
animate()
.alpha(1f)
- .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
@@ -555,15 +460,8 @@ public class AuthContainerView extends LinearLayout
}
private static boolean shouldUpdatePositionForUdfps(@NonNull View view) {
- if (view instanceof AuthBiometricUdfpsView) {
- return true;
- }
-
- if (view instanceof AuthBiometricFaceToFingerprintView) {
- AuthBiometricFaceToFingerprintView faceToFingerprintView =
- (AuthBiometricFaceToFingerprintView) view;
- return faceToFingerprintView.getActiveSensorType() == TYPE_FINGERPRINT
- && faceToFingerprintView.isFingerprintUdfps();
+ if (view instanceof AuthBiometricFingerprintView) {
+ return ((AuthBiometricFingerprintView) view).isUdfps();
}
return false;
@@ -652,12 +550,13 @@ public class AuthContainerView extends LinearLayout
}
@Override
- public void onAuthenticationSucceeded() {
- mBiometricView.onAuthenticationSucceeded();
+ public void onAuthenticationSucceeded(@Modality int modality) {
+ mBiometricView.onAuthenticationSucceeded(modality);
}
@Override
public void onAuthenticationFailed(@Modality int modality, String failureReason) {
+ mFailedModalities.add(modality);
mBiometricView.onAuthenticationFailed(modality, failureReason);
}
@@ -672,8 +571,17 @@ public class AuthContainerView extends LinearLayout
}
@Override
+ public void onPointerDown() {
+ if (mBiometricView.onPointerDown(mFailedModalities)) {
+ Log.d(TAG, "retrying failed modalities (pointer down)");
+ mBiometricCallback.onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
+ }
+ }
+
+ @Override
public void onSaveState(@NonNull Bundle outState) {
- outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState);
+ outState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY,
+ mContainerState == STATE_ANIMATING_OUT);
// In the case where biometric and credential are both allowed, we can assume that
// biometric isn't showing if credential is showing since biometric is shown first.
outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING,
@@ -695,8 +603,7 @@ public class AuthContainerView extends LinearLayout
mBiometricView.startTransitionToCredentialUI();
}
- @VisibleForTesting
- void animateAway(int reason) {
+ void animateAway(@AuthDialogCallback.DismissedReason int reason) {
animateAway(true /* sendReason */, reason);
}
@@ -724,31 +631,32 @@ public class AuthContainerView extends LinearLayout
removeWindowIfAttached();
};
+ final long animateDuration = mConfig.mSkipAnimation ? 0 : ANIMATION_DURATION_AWAY_MS;
postOnAnimation(() -> {
mPanelView.animate()
.translationY(mTranslationY)
- .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.withEndAction(endActionRunnable)
.start();
mBiometricScrollView.animate()
.translationY(mTranslationY)
- .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
mCredentialView.animate()
.translationY(mTranslationY)
- .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
}
animate()
.alpha(0f)
- .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
@@ -773,8 +681,7 @@ public class AuthContainerView extends LinearLayout
mWindowManager.removeView(this);
}
- @VisibleForTesting
- void onDialogAnimatedIn() {
+ private void onDialogAnimatedIn() {
if (mContainerState == STATE_PENDING_DISMISS) {
Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
@@ -788,8 +695,7 @@ public class AuthContainerView extends LinearLayout
}
@VisibleForTesting
- static WindowManager.LayoutParams getLayoutParams(IBinder windowToken,
- CharSequence title) {
+ static WindowManager.LayoutParams getLayoutParams(IBinder windowToken, CharSequence title) {
final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_SECURE;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -805,24 +711,4 @@ public class AuthContainerView extends LinearLayout
lp.token = windowToken;
return lp;
}
-
- // returns [face, fingerprint] sensor ids (id is -1 if not present)
- private int[] findFaceAndFingerprintSensors() {
- int faceSensorId = -1;
- int fingerprintSensorId = -1;
-
- for (final int sensorId : mConfig.mSensorIds) {
- if (Utils.containsSensorId(mFpProps, sensorId)) {
- fingerprintSensorId = sensorId;
- } else if (Utils.containsSensorId(mFaceProps, sensorId)) {
- faceSensorId = sensorId;
- }
-
- if (fingerprintSensorId != -1 && faceSensorId != -1) {
- break;
- }
- }
-
- return new int[] {faceSensorId, fingerprintSensorId};
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index dfb8c18b4ece..64c2d2e3858e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -51,6 +51,7 @@ import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.MotionEvent;
@@ -59,6 +60,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.CoreStartable;
import com.android.systemui.assist.ui.DisplayUtils;
import com.android.systemui.dagger.SysUISingleton;
@@ -123,8 +125,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Nullable private SidefpsController mSidefpsController;
@Nullable private IBiometricContextListener mBiometricContextListener;
@VisibleForTesting
- TaskStackListener mTaskStackListener;
- @VisibleForTesting
IBiometricSysuiReceiver mReceiver;
@VisibleForTesting
@NonNull final BiometricDisplayListener mOrientationListener;
@@ -137,13 +137,16 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private boolean mAllAuthenticatorsRegistered;
+ @NonNull private final UserManager mUserManager;
+ @NonNull private final LockPatternUtils mLockPatternUtils;
- private class BiometricTaskStackListener extends TaskStackListener {
+ @VisibleForTesting
+ final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
mHandler.post(AuthController.this::handleTaskStackChanged);
}
- }
+ };
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
@@ -256,6 +259,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
if (mUdfpsProps != null) {
mUdfpsController = mUdfpsControllerFactory.get();
+ mUdfpsController.addCallback(new UdfpsController.Callback() {
+ @Override
+ public void onFingerUp() {}
+
+ @Override
+ public void onFingerDown() {
+ if (mCurrentDialog != null) {
+ mCurrentDialog.onPointerDown();
+ }
+ }
+ });
}
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
if (mSidefpsProps != null) {
@@ -360,20 +374,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onStartFingerprintNow() {
- if (mReceiver == null) {
- Log.e(TAG, "onStartUdfpsNow: Receiver is null");
- return;
- }
-
- try {
- mReceiver.onStartFingerprintNow();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
- }
- }
-
- @Override
public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
@@ -503,12 +503,16 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory,
@NonNull DisplayManager displayManager,
- WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull UserManager userManager,
+ @NonNull LockPatternUtils lockPatternUtils,
@NonNull StatusBarStateController statusBarStateController,
@Main Handler handler) {
super(context);
mExecution = execution;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mUserManager = userManager;
+ mLockPatternUtils = lockPatternUtils;
mHandler = handler;
mCommandQueue = commandQueue;
mActivityTaskManager = activityTaskManager;
@@ -583,7 +587,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mFingerprintAuthenticatorsRegisteredCallback);
}
- mTaskStackListener = new BiometricTaskStackListener();
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
}
@@ -668,11 +671,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
* example, KeyguardUpdateMonitor has its own {@link FingerprintManager.AuthenticationCallback}.
*/
@Override
- public void onBiometricAuthenticated() {
+ public void onBiometricAuthenticated(@Modality int modality) {
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: ");
if (mCurrentDialog != null) {
- mCurrentDialog.onAuthenticationSucceeded();
+ mCurrentDialog.onAuthenticationSucceeded(modality);
} else {
Log.w(TAG, "onBiometricAuthenticated callback but dialog gone");
}
@@ -827,7 +830,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
final String opPackageName = (String) args.arg6;
final long operationId = args.argl1;
final long requestId = args.argl2;
- final @BiometricMultiSensorMode int multiSensorConfig = args.argi2;
+ @BiometricMultiSensorMode final int multiSensorConfig = args.argi2;
// Create a new dialog but do not replace the current one yet.
final AuthDialog newDialog = buildDialog(
@@ -835,13 +838,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
requireConfirmation,
userId,
sensorIds,
- credentialAllowed,
opPackageName,
skipAnimation,
operationId,
requestId,
multiSensorConfig,
- mWakefulnessLifecycle);
+ mWakefulnessLifecycle,
+ mUserManager,
+ mLockPatternUtils);
if (newDialog == null) {
Log.e(TAG, "Unsupported type configuration");
@@ -902,8 +906,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
// Only show the dialog if necessary. If it was animating out, the dialog is supposed
// to send its pending callback immediately.
- if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
- != AuthContainerView.STATE_ANIMATING_OUT) {
+ if (!savedState.getBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false)) {
final boolean credentialShowing =
savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING);
if (credentialShowing) {
@@ -927,10 +930,12 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
- int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName,
+ int userId, int[] sensorIds, String opPackageName,
boolean skipIntro, long operationId, long requestId,
@BiometricMultiSensorMode int multiSensorConfig,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ @NonNull WakefulnessLifecycle wakefulnessLifecycle,
+ @NonNull UserManager userManager,
+ @NonNull LockPatternUtils lockPatternUtils) {
return new AuthContainerView.Builder(mContext)
.setCallback(this)
.setPromptInfo(promptInfo)
@@ -941,7 +946,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
.setOperationId(operationId)
.setRequestId(requestId)
.setMultiSensorConfig(multiSensorConfig)
- .build(sensorIds, credentialAllowed, mFpProps, mFaceProps, wakefulnessLifecycle);
+ .build(sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, userManager,
+ lockPatternUtils);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index fa5213e94081..59ed156bce33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -31,7 +31,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public interface AuthDialog {
- String KEY_CONTAINER_STATE = "container_state";
+ String KEY_CONTAINER_GOING_AWAY = "container_going_away";
String KEY_BIOMETRIC_SHOWING = "biometric_showing";
String KEY_CREDENTIAL_SHOWING = "credential_showing";
@@ -64,7 +64,7 @@ public interface AuthDialog {
@interface DialogSize {}
/**
- * Parameters used when laying out {@link AuthBiometricView}, its sublclasses, and
+ * Parameters used when laying out {@link AuthBiometricView}, its subclasses, and
* {@link AuthPanelController}.
*/
class LayoutParams {
@@ -113,7 +113,7 @@ public interface AuthDialog {
/**
* Biometric authenticated. May be pending user confirmation, or completed.
*/
- void onAuthenticationSucceeded();
+ void onAuthenticationSucceeded(@Modality int modality);
/**
* Authentication failed (reject, timeout). Dialog stays showing.
@@ -136,6 +136,9 @@ public interface AuthDialog {
*/
void onError(@Modality int modality, String error);
+ /** UDFPS pointer down event. */
+ void onPointerDown();
+
/**
* Save the current state.
* @param outState
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index 9f40ca7b0346..a7d2901b21c3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -70,9 +70,4 @@ public interface AuthDialogCallback {
* Notifies when the dialog has finished animating.
*/
void onDialogAnimatedIn();
-
- /**
- * Notifies that the fingerprint sensor should be started now.
- */
- void onStartFingerprintNow();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index f82ea790bb64..99f27d7f48e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -40,8 +40,8 @@ import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
@@ -54,9 +54,9 @@ import javax.inject.Provider
* Controls the ripple effect that shows when authentication is successful.
* The ripple uses the accent color of the current theme.
*/
-@StatusBarScope
+@CentralSurfacesScope
class AuthRippleController @Inject constructor(
- private val statusBar: StatusBar,
+ private val centralSurfaces: CentralSurfaces,
private val sysuiContext: Context,
private val authController: AuthController,
private val configurationController: ConfigurationController,
@@ -137,7 +137,7 @@ class AuthRippleController @Inject constructor(
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val lightRevealScrim = statusBar.lightRevealScrim
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim?.revealEffect = it
@@ -155,7 +155,7 @@ class AuthRippleController @Inject constructor(
override fun onKeyguardFadingAwayChanged() {
if (keyguardStateController.isKeyguardFadingAway) {
- val lightRevealScrim = statusBar.lightRevealScrim
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
@@ -170,7 +170,7 @@ class AuthRippleController @Inject constructor(
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
- // Reset light reveal scrim to the default, so the StatusBar
+ // Reset light reveal scrim to the default, so the CentralSurfaces
// can handle any subsequent light reveal changes
// (ie: from dozing changes)
if (lightRevealScrim.revealEffect == circleReveal) {
@@ -199,8 +199,8 @@ class AuthRippleController @Inject constructor(
it.y,
0f,
Math.max(
- Math.max(it.x, statusBar.displayWidth - it.x),
- Math.max(it.y, statusBar.displayHeight - it.y)
+ Math.max(it.x, centralSurfaces.displayWidth - it.x),
+ Math.max(it.y, centralSurfaces.displayHeight - it.y)
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 7fdb5eab8701..9281eb8acb56 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -34,8 +34,10 @@ import android.widget.FrameLayout;
* - optionally can override dozeTimeTick to adjust views for burn-in mitigation
*/
public abstract class UdfpsAnimationView extends FrameLayout {
- // mAlpha takes into consideration the status bar expansion amount to fade out icon when
- // the status bar is expanded
+ private float mDialogSuggestedAlpha = 1f;
+ private float mNotificationShadeExpansion = 0f;
+
+ // mAlpha takes into consideration the status bar expansion amount and dialog suggested alpha
private int mAlpha;
boolean mPauseAuth;
@@ -92,6 +94,10 @@ public abstract class UdfpsAnimationView extends FrameLayout {
}
int calculateAlpha() {
+ int alpha = expansionToAlpha(mNotificationShadeExpansion);
+ alpha *= mDialogSuggestedAlpha;
+ mAlpha = alpha;
+
return mPauseAuth ? mAlpha : 255;
}
@@ -111,8 +117,26 @@ public abstract class UdfpsAnimationView extends FrameLayout {
return (int) ((1 - percent) * 255);
}
+ /**
+ * Set the suggested alpha based on whether a dialog was recently shown or hidden.
+ * @param dialogSuggestedAlpha value from 0f to 1f.
+ */
+ public void setDialogSuggestedAlpha(float dialogSuggestedAlpha) {
+ mDialogSuggestedAlpha = dialogSuggestedAlpha;
+ updateAlpha();
+ }
+
+ public float getDialogSuggestedAlpha() {
+ return mDialogSuggestedAlpha;
+ }
+
+ /**
+ * Sets the amount the notification shade is expanded. This will influence the opacity of the
+ * this visual affordance.
+ * @param expansion amount the shade has expanded from 0f to 1f.
+ */
public void onExpansionChanged(float expansion) {
- mAlpha = expansionToAlpha(expansion);
+ mNotificationShadeExpansion = expansion;
updateAlpha();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index c33cd8d537dc..27b0592af2d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -15,10 +15,12 @@
*/
package com.android.systemui.biometrics
+import android.animation.ValueAnimator
import android.graphics.PointF
import android.graphics.RectF
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.animation.Interpolators
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
@@ -50,7 +52,8 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
private val view: T
get() = mView!!
- private val dialogListener = SystemUIDialogManager.Listener { updatePauseAuth() }
+ private var dialogAlphaAnimator: ValueAnimator? = null
+ private val dialogListener = SystemUIDialogManager.Listener { runDialogAlphaAnimator() }
private val panelExpansionListener =
PanelExpansionListener { fraction, expanded, tracking ->
@@ -83,6 +86,29 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
*/
open val paddingY: Int = 0
+ open fun updateAlpha() {
+ view.updateAlpha()
+ }
+
+ fun runDialogAlphaAnimator() {
+ val hideAffordance = dialogManager.shouldHideAffordance()
+ dialogAlphaAnimator?.cancel()
+ dialogAlphaAnimator = ValueAnimator.ofFloat(
+ view.calculateAlpha() / 255f,
+ if (hideAffordance) 0f else 1f)
+ .apply {
+ duration = if (hideAffordance) 83L else 200L
+ interpolator = if (hideAffordance) Interpolators.LINEAR else Interpolators.ALPHA_IN
+
+ addUpdateListener { animatedValue ->
+ view.setDialogSuggestedAlpha(animatedValue.animatedValue as Float)
+ updateAlpha()
+ updatePauseAuth()
+ }
+ start()
+ }
+ }
+
override fun onViewAttached() {
panelExpansionStateManager.addExpansionListener(panelExpansionListener)
dialogManager.registerListener(dialogListener)
@@ -106,6 +132,7 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
pw.println("mNotificationShadeVisible=$notificationShadeVisible")
pw.println("shouldPauseAuth()=" + shouldPauseAuth())
pw.println("isPauseAuth=" + view.isPauseAuth)
+ pw.println("dialogSuggestedAlpha=" + view.dialogSuggestedAlpha)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
index 6607915fac9d..242601d46fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
@@ -23,7 +23,7 @@ import android.util.AttributeSet
*
* Currently doesn't draw anything.
*
- * Note that [AuthBiometricUdfpsView] also shows UDFPS animations. At some point we should
+ * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should
* de-dupe this if necessary.
*/
class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 5ac21ff42f4f..b8334a02e5f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -28,6 +28,7 @@ import android.view.MotionEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -112,6 +113,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mActivityLaunchAnimator = activityLaunchAnimator;
mUnlockedScreenOffDozeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mUnlockedScreenOffDozeAnimator.setInterpolator(Interpolators.ALPHA_IN);
mUnlockedScreenOffDozeAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -245,7 +247,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
return false;
}
- if (getDialogManager().shouldHideAffordance()) {
+ if (mView.getDialogSuggestedAlpha() == 0f) {
return true;
}
@@ -321,7 +323,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
* Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's
* alpha is based on the doze amount.
*/
- private void updateAlpha() {
+ @Override
+ public void updateAlpha() {
// fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
// the keyguard is occluded by some application - so instead use the input bouncer
// hidden amount to determine the fade
@@ -338,6 +341,10 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
if (mIsLaunchingActivity && !mUdfpsRequested) {
alpha *= (1.0f - mActivityLaunchProgress);
}
+
+ // Fade out alpha when a dialog is shown
+ // Fade in alpha when a dialog is hidden
+ alpha *= mView.getDialogSuggestedAlpha();
}
mView.setUnpausedAlpha(alpha);
}
@@ -369,6 +376,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
public void onStateChanged(int statusBarState) {
mStatusBarState = statusBarState;
mView.setStatusBarState(statusBarState);
+ updateAlpha();
updatePauseAuth();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
deleted file mode 100644
index 6989547dce52..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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.biometrics;
-
-import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import static android.hardware.biometrics.BiometricManager.Authenticators;
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.biometrics.PromptInfo;
-import android.hardware.biometrics.SensorPropertiesInternal;
-import android.os.UserManager;
-import android.util.DisplayMetrics;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.widget.LockPatternUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-public class Utils {
-
- public static final int CREDENTIAL_PIN = 1;
- public static final int CREDENTIAL_PATTERN = 2;
- public static final int CREDENTIAL_PASSWORD = 3;
-
- /** Base set of layout flags for fingerprint overlay widgets. */
- public static final int FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
- @interface CredentialType {}
-
- static float dpToPixels(Context context, float dp) {
- return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
- / DisplayMetrics.DENSITY_DEFAULT);
- }
-
- static void notifyAccessibilityContentChanged(AccessibilityManager am, ViewGroup view) {
- if (!am.isEnabled()) {
- return;
- }
- AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE);
- view.sendAccessibilityEventUnchecked(event);
- view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE);
- }
-
- static boolean isDeviceCredentialAllowed(PromptInfo promptInfo) {
- @Authenticators.Types final int authenticators = promptInfo.getAuthenticators();
- return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
- }
-
- static boolean isBiometricAllowed(PromptInfo promptInfo) {
- @Authenticators.Types final int authenticators = promptInfo.getAuthenticators();
- return (authenticators & Authenticators.BIOMETRIC_WEAK) != 0;
- }
-
- static @CredentialType int getCredentialType(Context context, int userId) {
- final LockPatternUtils lpu = new LockPatternUtils(context);
- switch (lpu.getKeyguardStoredPasswordQuality(userId)) {
- case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
- return CREDENTIAL_PATTERN;
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
- return CREDENTIAL_PIN;
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
- case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
- case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
- return CREDENTIAL_PASSWORD;
- default:
- return CREDENTIAL_PASSWORD;
- }
- }
-
- static boolean isManagedProfile(Context context, int userId) {
- final UserManager userManager = context.getSystemService(UserManager.class);
- return userManager.isManagedProfile(userId);
- }
-
- static boolean containsSensorId(@Nullable List<? extends SensorPropertiesInternal> properties,
- int sensorId) {
- if (properties == null) {
- return false;
- }
-
- for (SensorPropertiesInternal prop : properties) {
- if (prop.sensorId == sensorId) {
- return true;
- }
- }
-
- return false;
- }
-
- static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
- final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
- == PackageManager.PERMISSION_GRANTED;
- return hasPermission && "android".equals(clientPackage);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt
new file mode 100644
index 000000000000..d0d6f4cbf166
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.biometrics
+
+import android.Manifest
+import android.annotation.IntDef
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+import android.content.Context
+import android.content.pm.PackageManager
+import android.hardware.biometrics.BiometricManager.Authenticators
+import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.SensorPropertiesInternal
+import android.os.UserManager
+import android.util.DisplayMetrics
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import com.android.internal.widget.LockPatternUtils
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+
+object Utils {
+ const val CREDENTIAL_PIN = 1
+ const val CREDENTIAL_PATTERN = 2
+ const val CREDENTIAL_PASSWORD = 3
+
+ /** Base set of layout flags for fingerprint overlay widgets. */
+ const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
+ (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)
+
+ @JvmStatic
+ fun dpToPixels(context: Context, dp: Float): Float {
+ val density = context.resources.displayMetrics.densityDpi.toFloat()
+ return dp * (density / DisplayMetrics.DENSITY_DEFAULT)
+ }
+
+ @JvmStatic
+ fun notifyAccessibilityContentChanged(am: AccessibilityManager, view: ViewGroup) {
+ if (!am.isEnabled) {
+ return
+ }
+ val event = AccessibilityEvent.obtain()
+ event.eventType = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ event.contentChangeTypes =
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+ view.sendAccessibilityEventUnchecked(event)
+ view.notifySubtreeAccessibilityStateChanged(
+ view,
+ view,
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+ )
+ }
+
+ @JvmStatic
+ fun isDeviceCredentialAllowed(promptInfo: PromptInfo): Boolean =
+ (promptInfo.authenticators and Authenticators.DEVICE_CREDENTIAL) != 0
+
+ @JvmStatic
+ fun isBiometricAllowed(promptInfo: PromptInfo): Boolean =
+ (promptInfo.authenticators and Authenticators.BIOMETRIC_WEAK) != 0
+
+ @JvmStatic
+ @CredentialType
+ fun getCredentialType(utils: LockPatternUtils, userId: Int): Int =
+ when (utils.getKeyguardStoredPasswordQuality(userId)) {
+ PASSWORD_QUALITY_SOMETHING -> CREDENTIAL_PATTERN
+ PASSWORD_QUALITY_NUMERIC,
+ PASSWORD_QUALITY_NUMERIC_COMPLEX -> CREDENTIAL_PIN
+ PASSWORD_QUALITY_ALPHABETIC,
+ PASSWORD_QUALITY_ALPHANUMERIC,
+ PASSWORD_QUALITY_COMPLEX,
+ PASSWORD_QUALITY_MANAGED -> CREDENTIAL_PASSWORD
+ else -> CREDENTIAL_PASSWORD
+ }
+
+ @JvmStatic
+ fun isManagedProfile(context: Context, userId: Int): Boolean =
+ context.getSystemService(UserManager::class.java)?.isManagedProfile(userId) ?: false
+
+ @JvmStatic
+ fun <T : SensorPropertiesInternal> findFirstSensorProperties(
+ properties: List<T>?,
+ sensorIds: IntArray
+ ): T? = properties?.firstOrNull { sensorIds.contains(it.sensorId) }
+
+ @JvmStatic
+ fun isSystem(context: Context, clientPackage: String?): Boolean {
+ val hasPermission =
+ (context.checkCallingOrSelfPermission(Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED)
+ return hasPermission && "android" == clientPackage
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD)
+ internal annotation class CredentialType
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index d2ded71487dc..5b38e5b28be9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -97,7 +97,11 @@ class ControlsProviderLifecycleManager(
}
bindTryCount++
try {
- context.bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
+ val bound = context
+ .bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
+ if (!bound) {
+ context.unbindService(serviceConnection)
+ }
} catch (e: SecurityException) {
Log.e(TAG, "Failed to bind to service", e)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index b32f8786899a..2f041acb649d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -18,7 +18,7 @@ package com.android.systemui.dagger;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import dagger.Module;
@@ -27,7 +27,7 @@ import dagger.Module;
*/
@Module(includes = {
RecentsModule.class,
- StatusBarModule.class,
+ CentralSurfacesModule.class,
KeyguardModule.class,
})
public abstract class SystemUIBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index a4da6b422bde..5d154c3b4f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -48,7 +48,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.dagger.StartStatusBarModule;
+import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -87,7 +87,7 @@ import dagger.Provides;
MediaModule.class,
PowerModule.class,
QSModule.class,
- StartStatusBarModule.class,
+ StartCentralSurfacesModule.class,
VolumeModule.class
})
public abstract class SystemUIDefaultModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 13067bf71165..27993010c917 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -67,8 +67,8 @@ import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotifica
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -131,7 +131,7 @@ import dagger.Provides;
WalletModule.class
},
subcomponents = {
- StatusBarComponent.class,
+ CentralSurfacesComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
@@ -175,7 +175,7 @@ public abstract class SystemUIModule {
abstract Recents optionalRecents();
@BindsOptionalOf
- abstract StatusBar optionalStatusBar();
+ abstract CentralSurfaces optionalCentralSurfaces();
@BindsOptionalOf
abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 2beed4c6a7e7..d89c0be26351 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -31,7 +31,6 @@ public interface DozeHost {
boolean isPowerSaveActive();
boolean isPulsingBlocked();
boolean isProvisioned();
- boolean isBlockingDoze();
/**
* Makes a current pulse last for twice as long.
@@ -80,8 +79,9 @@ public interface DozeHost {
*/
void stopPulsing();
- /** Returns whether doze is suppressed. */
- boolean isDozeSuppressed();
+ /** Returns whether always-on-display is suppressed. This does not include suppressing
+ * wake-up gestures. */
+ boolean isAlwaysOnSuppressed();
interface Callback {
/**
@@ -97,8 +97,10 @@ public interface DozeHost {
*/
default void onPowerSaveChanged(boolean active) {}
- /** Called when the doze suppression state changes. */
- default void onDozeSuppressedChanged(boolean suppressed) {}
+ /**
+ * Called when the always on suppression state changes. See {@link #isAlwaysOnSuppressed()}.
+ */
+ default void onAlwaysOnSuppressedChanged(boolean suppressed) {}
}
interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 25115200ba19..0a2e69f943c6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -132,14 +132,6 @@ public class DozeLog implements Dumpable {
}
/**
- * Appends dozing event to the logs
- * @param suppressed true if dozing is suppressed
- */
- public void traceDozingSuppressed(boolean suppressed) {
- mLogger.logDozingSuppressed(suppressed);
- }
-
- /**
* Appends fling event to the logs
*/
public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
@@ -325,15 +317,40 @@ public class DozeLog implements Dumpable {
}
/**
- * Appends doze suppressed event to the logs
+ * Appends the doze state that was suppressed to the doze event log
* @param suppressedState The {@link DozeMachine.State} that was suppressed
*/
- public void traceDozeSuppressed(DozeMachine.State suppressedState) {
- mLogger.logDozeSuppressed(suppressedState);
+ public void traceAlwaysOnSuppressed(DozeMachine.State suppressedState) {
+ mLogger.logAlwaysOnSuppressed(suppressedState);
+ }
+
+ /**
+ * Appends reason why doze immediately ended.
+ */
+ public void traceImmediatelyEndDoze(String reason) {
+ mLogger.logImmediatelyEndDoze(reason);
+ }
+
+ /**
+ * Appends power save changes that may cause a new doze state
+ * @param powerSaveActive true if power saving is active
+ * @param nextState the state that we'll transition to
+ */
+ public void tracePowerSaveChanged(boolean powerSaveActive, DozeMachine.State nextState) {
+ mLogger.logPowerSaveChanged(powerSaveActive, nextState);
+ }
+
+ /**
+ * Appends an event on AOD suppression change
+ * @param suppressed true if AOD is being suppressed
+ * @param nextState the state that we'll transition to
+ */
+ public void traceAlwaysOnSuppressedChange(boolean suppressed, DozeMachine.State nextState) {
+ mLogger.logAlwaysOnSuppressedChange(suppressed, nextState);
}
/**
- * Appends new AOD sreen brightness to logs
+ * Appends new AOD screen brightness to logs
* @param brightness display brightness setting
*/
public void traceDozeScreenBrightness(int brightness) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 4ba6b51c83c8..f3f6be210fed 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -74,11 +74,21 @@ class DozeLogger @Inject constructor(
})
}
- fun logDozingSuppressed(isDozingSuppressed: Boolean) {
+ fun logPowerSaveChanged(powerSaveActive: Boolean, nextState: DozeMachine.State) {
buffer.log(TAG, INFO, {
- bool1 = isDozingSuppressed
+ bool1 = powerSaveActive
+ str1 = nextState.name
}, {
- "DozingSuppressed=$bool1"
+ "Power save active=$bool1 nextState=$str1"
+ })
+ }
+
+ fun logAlwaysOnSuppressedChange(isAodSuppressed: Boolean, nextState: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ bool1 = isAodSuppressed
+ str1 = nextState.name
+ }, {
+ "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1"
})
}
@@ -257,11 +267,19 @@ class DozeLogger @Inject constructor(
})
}
- fun logDozeSuppressed(state: DozeMachine.State) {
+ fun logAlwaysOnSuppressed(state: DozeMachine.State) {
buffer.log(TAG, INFO, {
str1 = state.name
}, {
- "Doze state suppressed, state=$str1"
+ "Always-on state suppressed, suppressed state=$str1"
+ })
+ }
+
+ fun logImmediatelyEndDoze(reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = reason
+ }, {
+ "Doze immediately ended due to $str1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 789ad6223e79..ae01f0ad84c7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -357,9 +357,9 @@ public class DozeMachine {
if (mState == State.FINISH) {
return State.FINISH;
}
- if (mDozeHost.isDozeSuppressed() && requestedState.isAlwaysOn()) {
+ if (mDozeHost.isAlwaysOnSuppressed() && requestedState.isAlwaysOn()) {
Log.i(TAG, "Doze is suppressed. Suppressing state: " + requestedState);
- mDozeLog.traceDozeSuppressed(requestedState);
+ mDozeLog.traceAlwaysOnSuppressed(requestedState);
return State.DOZE;
}
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
@@ -415,7 +415,6 @@ public class DozeMachine {
pw.print(" state="); pw.println(mState);
pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
pw.print(" wakeLock="); pw.println(mWakeLock);
- pw.print(" isDozeSuppressed="); pw.println(mDozeHost.isDozeSuppressed());
pw.println("Parts:");
for (Part p : mParts) {
p.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
new file mode 100644
index 000000000000..31d43b5475e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Handles suppressing doze on:
+ * 1. INITIALIZED, don't allow dozing at all when:
+ * - in CAR_MODE
+ * - device is NOT provisioned
+ * - there's a pending authentication
+ * 2. PowerSaveMode active
+ * - no always-on-display (DOZE_AOD)
+ * - continues to allow doze triggers (DOZE, DOZE_REQUEST_PULSE)
+ * 3. Suppression changes from the PowerManager API. See {@link PowerManager#suppressAmbientDisplay}
+ * and {@link DozeHost#isAlwaysOnSuppressed()}.
+ * - no always-on-display (DOZE_AOD)
+ * - allow doze triggers (DOZE), but disallow notifications (handled by {@link DozeTriggers})
+ * - See extra check in {@link DozeMachine} to guarantee device never enters always-on states
+ */
+@DozeScope
+public class DozeSuppressor implements DozeMachine.Part {
+ private static final String TAG = "DozeSuppressor";
+
+ private DozeMachine mMachine;
+ private final DozeHost mDozeHost;
+ private final AmbientDisplayConfiguration mConfig;
+ private final DozeLog mDozeLog;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UiModeManager mUiModeManager;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+
+ private boolean mBroadcastReceiverRegistered;
+
+ @Inject
+ public DozeSuppressor(
+ DozeHost dozeHost,
+ AmbientDisplayConfiguration config,
+ DozeLog dozeLog,
+ BroadcastDispatcher broadcastDispatcher,
+ UiModeManager uiModeManager,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy) {
+ mDozeHost = dozeHost;
+ mConfig = config;
+ mDozeLog = dozeLog;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mUiModeManager = uiModeManager;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ }
+
+ @Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case INITIALIZED:
+ registerBroadcastReceiver();
+ mDozeHost.addCallback(mHostCallback);
+ checkShouldImmediatelyEndDoze();
+ break;
+ case FINISH:
+ destroy();
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void destroy() {
+ unregisterBroadcastReceiver();
+ mDozeHost.removeCallback(mHostCallback);
+ }
+
+ private void checkShouldImmediatelyEndDoze() {
+ String reason = null;
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+ reason = "car_mode";
+ } else if (!mDozeHost.isProvisioned()) {
+ reason = "device_unprovisioned";
+ } else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
+ reason = "has_pending_auth";
+ }
+
+ if (!TextUtils.isEmpty(reason)) {
+ mDozeLog.traceImmediatelyEndDoze(reason);
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println(" uiMode=" + mUiModeManager.getCurrentModeType());
+ pw.println(" hasPendingAuth="
+ + mBiometricUnlockControllerLazy.get().hasPendingAuthentication());
+ pw.println(" isProvisioned=" + mDozeHost.isProvisioned());
+ pw.println(" isAlwaysOnSuppressed=" + mDozeHost.isAlwaysOnSuppressed());
+ pw.println(" aodPowerSaveActive=" + mDozeHost.isPowerSaveActive());
+ }
+
+ private void registerBroadcastReceiver() {
+ if (mBroadcastReceiverRegistered) {
+ return;
+ }
+ IntentFilter filter = new IntentFilter(ACTION_ENTER_CAR_MODE);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
+ mBroadcastReceiverRegistered = true;
+ }
+
+ private void unregisterBroadcastReceiver() {
+ if (!mBroadcastReceiverRegistered) {
+ return;
+ }
+ mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mBroadcastReceiverRegistered = false;
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+ mDozeLog.traceImmediatelyEndDoze("car_mode");
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+ }
+ };
+
+ private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
+ @Override
+ public void onPowerSaveChanged(boolean active) {
+ DozeMachine.State nextState = null;
+ if (mDozeHost.isPowerSaveActive()) {
+ nextState = DozeMachine.State.DOZE;
+ } else if (mMachine.getState() == DozeMachine.State.DOZE
+ && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ }
+
+ if (nextState != null) {
+ mDozeLog.tracePowerSaveChanged(mDozeHost.isPowerSaveActive(), nextState);
+ mMachine.requestState(nextState);
+ }
+ }
+
+ @Override
+ public void onAlwaysOnSuppressedChanged(boolean suppressed) {
+ final DozeMachine.State nextState;
+ if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ } else {
+ nextState = DozeMachine.State.DOZE;
+ }
+ mDozeLog.traceAlwaysOnSuppressedChange(suppressed, nextState);
+ mMachine.requestState(nextState);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 8bff3ba0b6ba..74044e2c2eb8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -17,12 +17,10 @@
package com.android.systemui.doze;
import android.annotation.Nullable;
-import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -88,7 +86,6 @@ public class DozeTriggers implements DozeMachine.Part {
private final AsyncSensorManager mSensorManager;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
- private final UiModeManager mUiModeManager;
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
@@ -203,8 +200,6 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
secureSettings, authController, devicePostureController);
-
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
mDozeLog = dozeLog;
@@ -247,7 +242,7 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
}
- if (mDozeHost.isDozeSuppressed()) {
+ if (mDozeHost.isAlwaysOnSuppressed()) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("dozeSuppressed");
return;
@@ -456,10 +451,9 @@ public class DozeTriggers implements DozeMachine.Part {
mAodInterruptRunnable = null;
sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
- mDozeHost.addCallback(mHostCallback);
mDockManager.addListener(mDockEventListener);
mDozeSensors.requestTemporaryDisable();
- checkTriggersAtInit();
+ mDozeHost.addCallback(mHostCallback);
break;
case DOZE:
case DOZE_AOD:
@@ -516,15 +510,6 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
-
- private void checkTriggersAtInit() {
- if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
- || mDozeHost.isBlockingDoze()
- || !mDozeHost.isProvisioned()) {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
- }
-
private void requestPulse(final int reason, boolean performedProxCheck,
Runnable onPulseSuppressedListener) {
Assert.isMainThread();
@@ -608,9 +593,6 @@ public class DozeTriggers implements DozeMachine.Part {
requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSuppressedListener */);
}
- if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
mDozeSensors.onUserSwitched();
}
@@ -621,7 +603,6 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
filter.addAction(Intent.ACTION_USER_SWITCHED);
broadcastDispatcher.registerReceiver(this, filter);
mRegistered = true;
@@ -659,26 +640,5 @@ public class DozeTriggers implements DozeMachine.Part {
public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
onNotification(onPulseSuppressedListener);
}
-
- @Override
- public void onPowerSaveChanged(boolean active) {
- if (mDozeHost.isPowerSaveActive()) {
- mMachine.requestState(DozeMachine.State.DOZE);
- } else if (mMachine.getState() == DozeMachine.State.DOZE
- && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
- mMachine.requestState(DozeMachine.State.DOZE_AOD);
- }
- }
-
- @Override
- public void onDozeSuppressedChanged(boolean suppressed) {
- final DozeMachine.State nextState;
- if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
- nextState = DozeMachine.State.DOZE_AOD;
- } else {
- nextState = DozeMachine.State.DOZE;
- }
- mMachine.requestState(nextState);
- }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 7e1fce298fbd..ebc766635733 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -19,6 +19,8 @@ package com.android.systemui.dreams;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -177,9 +179,26 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
mDreamOverlayContainerViewController.init();
+ // Make extra sure the container view has been removed from its old parent (otherwise we
+ // risk an IllegalStateException in some cases when setting the container view as the
+ // window's content view and the container view hasn't been properly removed previously).
+ removeContainerViewFromParent();
mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView());
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
}
+
+ private void removeContainerViewFromParent() {
+ View containerView = mDreamOverlayContainerViewController.getContainerView();
+ if (containerView == null) {
+ return;
+ }
+ ViewGroup parentView = (ViewGroup) containerView.getParent();
+ if (parentView == null) {
+ return;
+ }
+ Log.w(TAG, "Removing dream overlay container view parent!");
+ parentView.removeView(containerView);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 2d969206b468..d2ab61149d26 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -16,23 +16,44 @@
package com.android.systemui.dreams;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
-import com.android.internal.util.Preconditions;
import com.android.systemui.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
/**
* {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
* dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
*/
public class DreamOverlayStatusBarView extends ConstraintLayout {
- private ImageView mWifiStatusView;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATUS_ICON_" }, value = {
+ STATUS_ICON_NOTIFICATIONS,
+ STATUS_ICON_WIFI_UNAVAILABLE,
+ STATUS_ICON_ALARM_SET,
+ STATUS_ICON_MIC_CAMERA_DISABLED,
+ STATUS_ICON_PRIORITY_MODE_ON
+ })
+ public @interface StatusIconType {}
+ public static final int STATUS_ICON_NOTIFICATIONS = 0;
+ public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1;
+ public static final int STATUS_ICON_ALARM_SET = 2;
+ public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 3;
+ public static final int STATUS_ICON_PRIORITY_MODE_ON = 4;
+
+ private final Map<Integer, View> mStatusIcons = new HashMap<>();
public DreamOverlayStatusBarView(Context context) {
this(context, null);
@@ -55,16 +76,35 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
protected void onFinishInflate() {
super.onFinishInflate();
- mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
- "R.id.dream_overlay_wifi_status must not be null");
+ mStatusIcons.put(STATUS_ICON_WIFI_UNAVAILABLE,
+ fetchStatusIconForResId(R.id.dream_overlay_wifi_status));
+ mStatusIcons.put(STATUS_ICON_ALARM_SET,
+ fetchStatusIconForResId(R.id.dream_overlay_alarm_set));
+ mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED,
+ fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off));
+ mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
+ fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
+ mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
+ fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
+ }
+
+ void showIcon(@StatusIconType int iconType, boolean show) {
+ showIcon(iconType, show, null);
+ }
+
+ void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
+ View icon = mStatusIcons.get(iconType);
+ if (icon == null) {
+ return;
+ }
+ if (show && contentDescription != null) {
+ icon.setContentDescription(contentDescription);
+ }
+ icon.setVisibility(show ? View.VISIBLE : View.GONE);
}
- /**
- * Whether to show the wifi status icon.
- * @param show True if the wifi status icon should be shown.
- */
- void showWifiStatus(boolean show) {
- // Only show the wifi status icon when wifi isn't available.
- mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ private View fetchStatusIconForResId(int resId) {
+ final View statusIcon = findViewById(resId);
+ return Objects.requireNonNull(statusIcon);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 32b2309ee83b..a25a7423770e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -16,19 +16,35 @@
package com.android.systemui.dreams;
-import android.annotation.IntDef;
+import android.app.AlarmManager;
+import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.text.format.DateFormat;
+import android.util.PluralsMessageFormatter;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.time.DateFormatUtil;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Map;
import javax.inject.Inject;
@@ -37,19 +53,15 @@ import javax.inject.Inject;
*/
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "WIFI_STATUS_" }, value = {
- WIFI_STATUS_UNKNOWN,
- WIFI_STATUS_UNAVAILABLE,
- WIFI_STATUS_AVAILABLE
- })
- private @interface WifiStatus {}
- private static final int WIFI_STATUS_UNKNOWN = 0;
- private static final int WIFI_STATUS_UNAVAILABLE = 1;
- private static final int WIFI_STATUS_AVAILABLE = 2;
-
private final ConnectivityManager mConnectivityManager;
private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
+ private final NextAlarmController mNextAlarmController;
+ private final AlarmManager mAlarmManager;
+ private final Resources mResources;
+ private final DateFormatUtil mDateFormatUtil;
+ private final IndividualSensorPrivacyController mSensorPrivacyController;
+ private final NotificationListener mNotificationListener;
+ private final ZenModeController mZenModeController;
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
@@ -59,61 +71,183 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
- onWifiAvailabilityChanged(
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ updateWifiUnavailableStatusIcon();
}
@Override
public void onAvailable(Network network) {
- onWifiAvailabilityChanged(true);
+ updateWifiUnavailableStatusIcon();
}
@Override
public void onLost(Network network) {
- onWifiAvailabilityChanged(false);
+ updateWifiUnavailableStatusIcon();
}
};
- private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private final IndividualSensorPrivacyController.Callback mSensorCallback =
+ (sensor, blocked) -> updateMicCameraBlockedStatusIcon();
+
+ private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
+ nextAlarm -> updateAlarmStatusIcon();
+
+ private final NotificationHandler mNotificationHandler = new NotificationHandler() {
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRemoved(
+ StatusBarNotification sbn,
+ RankingMap rankingMap,
+ int reason) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
+ }
+
+ @Override
+ public void onNotificationsInitialized() {
+ updateNotificationsStatusIcon();
+ }
+ };
+
+ private final ZenModeController.Callback mZenModeCallback = new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ updatePriorityModeStatusIcon();
+ }
+ };
@Inject
public DreamOverlayStatusBarViewController(
DreamOverlayStatusBarView view,
+ @Main Resources resources,
ConnectivityManager connectivityManager,
- TouchInsetManager.TouchInsetSession touchInsetSession) {
+ TouchInsetManager.TouchInsetSession touchInsetSession,
+ AlarmManager alarmManager,
+ NextAlarmController nextAlarmController,
+ DateFormatUtil dateFormatUtil,
+ IndividualSensorPrivacyController sensorPrivacyController,
+ NotificationListener notificationListener,
+ ZenModeController zenModeController) {
super(view);
+ mResources = resources;
mConnectivityManager = connectivityManager;
mTouchInsetSession = touchInsetSession;
+ mAlarmManager = alarmManager;
+ mNextAlarmController = nextAlarmController;
+ mDateFormatUtil = dateFormatUtil;
+ mSensorPrivacyController = sensorPrivacyController;
+ mNotificationListener = notificationListener;
+ mZenModeController = zenModeController;
+
+ // Handlers can be added to NotificationListener, but apparently they can't be removed. So
+ // add the handler here in the constructor rather than in onViewAttached to avoid confusion.
+ mNotificationListener.addNotificationHandler(mNotificationHandler);
}
@Override
protected void onViewAttached() {
+ updateNotificationsStatusIcon();
+
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+ updateWifiUnavailableStatusIcon();
+
+ mNextAlarmController.addCallback(mNextAlarmCallback);
+ updateAlarmStatusIcon();
+
+ mSensorPrivacyController.addCallback(mSensorCallback);
+ updateMicCameraBlockedStatusIcon();
+
+ mZenModeController.addCallback(mZenModeCallback);
+ updatePriorityModeStatusIcon();
- NetworkCapabilities capabilities =
- mConnectivityManager.getNetworkCapabilities(
- mConnectivityManager.getActiveNetwork());
- onWifiAvailabilityChanged(
- capabilities != null
- && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
mTouchInsetSession.addViewToTracking(mView);
}
@Override
protected void onViewDetached() {
+ mZenModeController.removeCallback(mZenModeCallback);
+ mSensorPrivacyController.removeCallback(mSensorCallback);
+ mNextAlarmController.removeCallback(mNextAlarmCallback);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mTouchInsetSession.clear();
}
- /**
- * Wifi availability has changed. Update the wifi status icon as appropriate.
- * @param available Whether wifi is available.
- */
- private void onWifiAvailabilityChanged(boolean available) {
- final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
- if (mWifiStatus != newWifiStatus) {
- mWifiStatus = newWifiStatus;
- mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ private void updateWifiUnavailableStatusIcon() {
+ final NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ final boolean available = capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ mView.showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+ }
+
+ private void updateAlarmStatusIcon() {
+ final AlarmManager.AlarmClockInfo alarm =
+ mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
+ hasAlarm,
+ hasAlarm ? buildAlarmContentDescription(alarm) : null);
+ }
+
+ private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
+ final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ final String dateString = DateFormat.format(pattern, alarm.getTriggerTime()).toString();
+
+ return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
+ }
+
+ private void updateMicCameraBlockedStatusIcon() {
+ final boolean micBlocked = mSensorPrivacyController
+ .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
+ final boolean cameraBlocked = mSensorPrivacyController
+ .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ micBlocked && cameraBlocked);
+ }
+
+ private void updateNotificationsStatusIcon() {
+ if (mView == null) {
+ // It is possible for this method to be called before the view is attached, which makes
+ // null-checking necessary.
+ return;
}
+
+ final StatusBarNotification[] notifications =
+ mNotificationListener.getActiveNotifications();
+ final int notificationCount = notifications != null ? notifications.length : 0;
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
+ notificationCount > 0,
+ notificationCount > 0
+ ? buildNotificationsContentDescription(notificationCount)
+ : null);
+ }
+
+ private String buildNotificationsContentDescription(int notificationCount) {
+ return PluralsMessageFormatter.format(
+ mResources,
+ Map.of("count", notificationCount),
+ R.string.dream_overlay_status_bar_notification_indicator);
+ }
+
+ private void updatePriorityModeStatusIcon() {
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
+ mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index d16c8c8c59d6..e140f6b0faa2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -21,6 +21,9 @@ import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputEvent;
@@ -29,7 +32,7 @@ import android.view.VelocityTracker;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -68,13 +71,15 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private float mCurrentExpansion;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private VelocityTracker mVelocityTracker;
private final FlingAnimationUtils mFlingAnimationUtils;
private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private final DisplayMetrics mDisplayMetrics;
+
private Boolean mCapture;
private TouchSession mTouchSession;
@@ -85,40 +90,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
- boolean mTrack;
- boolean mBouncerPresent;
-
- @Override
- public boolean onDown(MotionEvent e) {
- // We only consider gestures that originate from the lower portion of the
- // screen.
- final float displayHeight = mStatusBar.getDisplayHeight();
-
- mBouncerPresent = mStatusBar.isBouncerShowing();
-
- // The target zone is either at the top or bottom of the screen, dependent on
- // whether the bouncer is present.
- final float zonePercentage =
- Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight))
- / displayHeight;
-
- mTrack = zonePercentage < mBouncerZoneScreenPercentage;
-
- // Never capture onDown. While this might lead to some false positive touches
- // being sent to other windows/layers, this is necessary to make sure the
- // proper touch event sequence is received by others in the event we do not
- // consume the sequence here.
- return false;
- }
-
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
- // Do not handle scroll gestures if not tracking touch events.
- if (!mTrack) {
- return false;
- }
-
if (mCapture == null) {
// If the user scrolling favors a vertical direction, begin capturing
// scrolls.
@@ -140,10 +114,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
// is fully hidden at full expansion (1) and fully visible when fully collapsed
// (0).
final float screenTravelPercentage =
- Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight());
- setPanelExpansion(
- mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage);
-
+ Math.abs((e1.getY() - e2.getY()) / mCentralSurfaces.getDisplayHeight());
+ setPanelExpansion(mCentralSurfaces.isBouncerShowing()
+ ? screenTravelPercentage : 1 - screenTravelPercentage);
return true;
}
};
@@ -155,8 +128,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
@Inject
public BouncerSwipeTouchHandler(
+ DisplayMetrics displayMetrics,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
NotificationShadeWindowController notificationShadeWindowController,
ValueAnimatorCreator valueAnimatorCreator,
VelocityTrackerFactory velocityTrackerFactory,
@@ -165,7 +139,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
@Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
FlingAnimationUtils flingAnimationUtilsClosing,
@Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
- mStatusBar = statusBar;
+ mDisplayMetrics = displayMetrics;
+ mCentralSurfaces = centralSurfaces;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationShadeWindowController = notificationShadeWindowController;
mBouncerZoneScreenPercentage = swipeRegionPercentage;
@@ -176,6 +151,21 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
}
@Override
+ public void getTouchInitiationRegion(Region region) {
+ if (mCentralSurfaces.isBouncerShowing()) {
+ region.op(new Rect(0, 0, mDisplayMetrics.widthPixels,
+ Math.round(mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
+ Region.Op.UNION);
+ } else {
+ region.op(new Rect(0,
+ Math.round(mDisplayMetrics.heightPixels * (1 - mBouncerZoneScreenPercentage)),
+ mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels),
+ Region.Op.UNION);
+ }
+ }
+
+ @Override
public void onSessionStart(TouchSession session) {
mVelocityTracker = mVelocityTrackerFactory.obtain();
mTouchSession = session;
@@ -202,7 +192,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
final MotionEvent motionEvent = (MotionEvent) event;
switch(motionEvent.getAction()) {
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
+ mTouchSession.pop();
// If we are not capturing any input, there is no need to consider animating to
// finish transition.
if (mCapture == null || !mCapture) {
@@ -226,7 +218,6 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
mStatusBarKeyguardViewManager.reset(false);
}
- mTouchSession.pop();
break;
default:
mVelocityTracker.addMovement(motionEvent);
@@ -255,7 +246,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
}
protected void flingToExpansion(float velocity, float expansion) {
- final float viewHeight = mStatusBar.getDisplayHeight();
+ final float viewHeight = mCentralSurfaces.getDisplayHeight();
final float currentHeight = viewHeight * mCurrentExpansion;
final float targetHeight = viewHeight * expansion;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 3e5efb2115a0..695b59ac45ad 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import android.view.InputEvent;
import android.view.MotionEvent;
@@ -34,6 +35,7 @@ import com.android.systemui.shared.system.InputChannelCompat;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -100,6 +102,10 @@ public class DreamOverlayTouchMonitor {
});
}
+ private int getSessionCount() {
+ return mActiveTouchSessions.size();
+ }
+
/**
* {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
* {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
@@ -146,6 +152,11 @@ public class DreamOverlayTouchMonitor {
return mTouchMonitor.pop(this);
}
+ @Override
+ public int getActiveSessionCount() {
+ return mTouchMonitor.getSessionCount();
+ }
+
/**
* Returns the active listeners to receive touch events.
*/
@@ -229,12 +240,39 @@ public class DreamOverlayTouchMonitor {
public void onInputEvent(InputEvent ev) {
// No Active sessions are receiving touches. Create sessions for each listener
if (mActiveTouchSessions.isEmpty()) {
+ final HashMap<DreamTouchHandler, DreamTouchHandler.TouchSession> sessionMap =
+ new HashMap<>();
+
for (DreamTouchHandler handler : mHandlers) {
+ final Region initiationRegion = Region.obtain();
+ handler.getTouchInitiationRegion(initiationRegion);
+
+ if (!initiationRegion.isEmpty()) {
+ // Initiation regions require a motion event to determine pointer location
+ // within the region.
+ if (!(ev instanceof MotionEvent)) {
+ continue;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) ev;
+
+ // If the touch event is outside the region, then ignore.
+ if (!initiationRegion.contains(Math.round(motionEvent.getX()),
+ Math.round(motionEvent.getY()))) {
+ continue;
+ }
+ }
+
final TouchSessionImpl sessionStack =
new TouchSessionImpl(DreamOverlayTouchMonitor.this, null);
mActiveTouchSessions.add(sessionStack);
- handler.onSessionStart(sessionStack);
+ sessionMap.put(handler, sessionStack);
}
+
+ // Informing handlers of new sessions is delayed until we have all created so the
+ // final session is correct.
+ sessionMap.forEach((dreamTouchHandler, touchSession)
+ -> dreamTouchHandler.onSessionStart(touchSession));
}
// Find active sessions and invoke on InputEvent.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
index c73ff733854d..20008d5b02c8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -71,6 +72,19 @@ public interface DreamTouchHandler {
* if the popped {@link TouchSession} was the initial session or has already been popped.
*/
ListenableFuture<TouchSession> pop();
+
+ /**
+ * Returns the number of currently active sessions.
+ */
+ int getActiveSessionCount();
+ }
+
+ /**
+ * Returns the region the touch handler is interested in. By default, no region is specified,
+ * indicating the entire screen should be considered.
+ * @param region A {@link Region} that is passed in to the target entry touch region.
+ */
+ default void getTouchInitiationRegion(Region region) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index e746cafb5ea0..74d5bd577cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -33,7 +33,7 @@ import javax.inject.Inject;
import javax.inject.Provider;
/**
- * Manages power menu plugins and communicates power menu actions to the StatusBar.
+ * Manages power menu plugins and communicates power menu actions to the CentralSurfaces.
*/
@SysUISingleton
public class GlobalActionsComponent extends CoreStartable
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index e3886cd80a42..7a278f786a67 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -121,7 +121,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -236,7 +236,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -344,7 +344,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
RingerModeTracker ringerModeTracker,
@Main Handler handler,
PackageManager packageManager,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
@@ -374,7 +374,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mRingerModeTracker = ringerModeTracker;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -426,8 +426,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
- protected Optional<StatusBar> getStatusBar() {
- return mStatusBarOptional;
+ protected Optional<CentralSurfaces> getCentralSurfaces() {
+ return mCentralSurfacesOptional;
}
protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
@@ -675,7 +675,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
- mPowerAdapter, mUiEventLogger, mStatusBarOptional, mKeyguardUpdateMonitor,
+ mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor,
mLockPatternUtils);
dialog.setOnDismissListener(this);
@@ -866,7 +866,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
if (mTelecomManager != null) {
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -998,7 +998,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mIActivityManager.requestInteractiveBugReport();
}
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
} catch (RemoteException e) {
}
}
@@ -1018,7 +1018,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
mIActivityManager.requestFullBugReport();
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
} catch (RemoteException e) {
}
return false;
@@ -2160,7 +2160,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
- private Optional<StatusBar> mStatusBarOptional;
+ private Optional<CentralSurfaces> mCentralSurfacesOptional;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private LockPatternUtils mLockPatternUtils;
private float mWindowDimAmount;
@@ -2188,8 +2188,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceY < 0 && distanceY > distanceX
- && e1.getY() <= mStatusBarOptional.map(
- StatusBar::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mCentralSurfacesOptional.map(
+ CentralSurfaces::getStatusBarHeight).orElse(0)) {
// Downwards scroll from top
openShadeAndDismiss();
return true;
@@ -2201,8 +2201,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
- && e1.getY() <= mStatusBarOptional.map(
- StatusBar::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mCentralSurfacesOptional.map(
+ CentralSurfaces::getStatusBarHeight).orElse(0)) {
// Downwards fling from top
openShadeAndDismiss();
return true;
@@ -2217,7 +2217,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
NotificationShadeWindowController notificationShadeWindowController,
Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ Optional<CentralSurfaces> centralSurfacesOptional,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
@@ -2232,7 +2233,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2262,14 +2263,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private void openShadeAndDismiss() {
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ if (mCentralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) {
// match existing lockscreen behavior to open QS when swiping from status bar
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.animateExpandSettingsPanel(null));
+ mCentralSurfacesOptional.ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null));
} else {
// otherwise, swiping down should expand notification shade
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.animateExpandNotificationsPanel());
+ mCentralSurfacesOptional.ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandNotificationsPanel());
}
dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 582965a12528..35f29b94966f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -23,6 +23,7 @@ import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Handler
+import android.util.Log
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
@@ -47,6 +48,8 @@ import dagger.Lazy
import javax.inject.Inject
import kotlin.math.min
+const val TAG = "KeyguardUnlock"
+
/**
* Starting scale factor for the app/launcher surface behind the keyguard, when it's animating
* in during keyguard exit.
@@ -584,8 +587,16 @@ class KeyguardUnlockAnimationController @Inject constructor(
* animation.
*/
fun hideKeyguardViewAfterRemoteAnimation() {
- // Hide the keyguard, with no fade out since we animated it away during the unlock.
- keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 0 /* fadeOutDuration */)
+ if (keyguardViewController.isShowing) {
+ // Hide the keyguard, with no fade out since we animated it away during the unlock.
+ keyguardViewController.hide(
+ surfaceBehindRemoteAnimationStartTime,
+ 0 /* fadeOutDuration */
+ )
+ } else {
+ Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
+ "showing. Ignoring...")
+ }
}
private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ae7147ecae03..c01d2c316a93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -127,7 +127,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -1667,11 +1667,16 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
return;
}
- // if the keyguard is already showing, don't bother. check flags in both files
- // to account for the hiding animation which results in a delay and discrepancy
- // between flags
- if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ // If the keyguard is already showing, don't bother unless it was in the process of going
+ // away. If it was going away, keyguard state may be out of sync and we should make sure to
+ // re-show it explicitly. Check flags in both files to account for the hiding animation
+ // which results in a delay and discrepancy between flags.
+ if ((mShowing && mKeyguardViewControllerLazy.get().isShowing())
+ && !mKeyguardStateController.isKeyguardGoingAway()) {
+ if (DEBUG) {
+ Log.d(TAG, "doKeyguard: not showing "
+ + "because it is already showing and not going away");
+ }
resetStateLocked();
return;
}
@@ -2186,7 +2191,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardExitAnimationRunner = null;
mScreenOnCoordinator.setWakeAndUnlocking(false);
mPendingLock = false;
- setShowingLocked(true);
+
+ // If we're asked to re-show while the keyguard is going away, force callbacks to ensure
+ // that state is re-set correctly. Otherwise, we might short circuit since mShowing is
+ // true during the keyguard going away process, despite having partially set some state
+ // to unlocked.
+ setShowingLocked(
+ true, mKeyguardStateController.isKeyguardGoingAway() /* forceCallbacks */);
+
mKeyguardViewControllerLazy.get().show(options);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2356,14 +2368,28 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void onAnimationFinished() throws RemoteException {
try {
+ // WindowManager always needs to know that this animation
+ // finished so it does not wait the 10s until timeout.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
- onKeyguardExitFinished();
- mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
- 0 /* fadeoutDuration */);
- mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+
+ // If we're not interactive, it means the device is going back to
+ // sleep. This happens if the power button is pressed during the
+ // activity launch. If we're going back to sleep, we should *not*
+ // run keyguard exit finished callbacks and hide the keyguard, since
+ // we are in the process of locking again and this might result in
+ // the device staying unlocked when it shouldn't.
+ // We need to directly query isInteractive rather than mGoingToSleep
+ // because mGoingToSleep is set in onStartedGoingToSleep, which is
+ // dispatched asynchronously.
+ if (mPM.isInteractive()) {
+ onKeyguardExitFinished();
+ mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
+ 0 /* fadeoutDuration */);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ }
}
@Override
@@ -2717,22 +2743,22 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Registers the StatusBar to which the Keyguard View is mounted.
+ * Registers the CentralSurfaces to which the Keyguard View is mounted.
*
- * @param statusBar
+ * @param centralSurfaces
* @param panelView
* @param biometricUnlockController
* @param notificationContainer
* @param bypassController
* @return the View Controller for the Keyguard View this class is mediating.
*/
- public KeyguardViewController registerStatusBar(StatusBar statusBar,
+ public KeyguardViewController registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController panelView,
@Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
- mKeyguardViewControllerLazy.get().registerStatusBar(
- statusBar,
+ mKeyguardViewControllerLazy.get().registerCentralSurfaces(
+ centralSurfaces,
panelView,
panelExpansionStateManager,
biometricUnlockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 195ef1a84c43..c69f947f5f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -48,10 +48,10 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
@@ -64,7 +64,7 @@ import dagger.Module;
import dagger.Provides;
/**
- * Dagger Module providing {@link StatusBar}.
+ * Dagger Module providing keyguard.
*/
@Module(subcomponents = {
KeyguardQsUserSwitchComponent.class,
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 b32358643efb..ae7a671f3d54 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -154,6 +154,28 @@ public class LogModule {
return factory.create("SwipeStatusBarAwayLog", 30);
}
+ /**
+ * Provides a logging buffer for logs related to the media tap-to-transfer chip on the sender
+ * device. See {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
+ */
+ @Provides
+ @SysUISingleton
+ @MediaTttSenderLogBuffer
+ public static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttSender", 20);
+ }
+
+ /**
+ * Provides a logging buffer for logs related to the media tap-to-transfer chip on the receiver
+ * device. See {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ */
+ @Provides
+ @SysUISingleton
+ @MediaTttReceiverLogBuffer
+ public static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttReceiver", 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/log/dagger/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
new file mode 100644
index 000000000000..5c572e8ef554
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.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.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface MediaTttReceiverLogBuffer {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
new file mode 100644
index 000000000000..edab8c319f87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.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.media.taptotransfer.sender.MediaTttSenderLogger}.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface MediaTttSenderLogBuffer {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 7b850504421d..fab06c288ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -550,12 +550,12 @@ class MediaDataManager(
// Album art
val notif: Notification = sbn.notification
- var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
+ var artworkBitmap = metadata?.let { loadBitmapFromUri(it) }
if (artworkBitmap == null) {
- artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
+ artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
}
- if (artworkBitmap == null && metadata != null) {
- artworkBitmap = loadBitmapFromUri(metadata)
+ if (artworkBitmap == null) {
+ artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
}
val artWorkIcon = if (artworkBitmap == null) {
notif.getLargeIcon()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index e2716e992c48..77873e829be3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -38,12 +38,10 @@ import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
-import android.view.View;
import android.view.Window;
-import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
public class MediaProjectionPermissionActivity extends Activity
@@ -56,7 +54,7 @@ public class MediaProjectionPermissionActivity extends Activity
private int mUid;
private IMediaProjectionManager mService;
- private AlertDialog mDialog;
+ private SystemUIDialog mDialog;
@Override
public void onCreate(Bundle icicle) {
@@ -143,25 +141,18 @@ public class MediaProjectionPermissionActivity extends Activity
dialogTitle = getString(R.string.media_projection_dialog_title, appName);
}
- View dialogTitleView = View.inflate(this, R.layout.media_projection_dialog_title, null);
- TextView titleText = (TextView) dialogTitleView.findViewById(R.id.dialog_title);
- titleText.setText(dialogTitle);
-
- mDialog = new AlertDialog.Builder(this)
- .setCustomTitle(dialogTitleView)
- .setMessage(dialogText)
- .setPositiveButton(R.string.media_projection_action_text, this)
- .setNegativeButton(android.R.string.cancel, this)
- .setOnCancelListener(this)
- .create();
+ mDialog = new SystemUIDialog(this);
+ mDialog.setTitle(dialogTitle);
+ mDialog.setIcon(R.drawable.ic_media_projection_permission);
+ mDialog.setMessage(dialogText);
+ mDialog.setPositiveButton(R.string.media_projection_action_text, this);
+ mDialog.setNeutralButton(android.R.string.cancel, this);
+ mDialog.setOnCancelListener(this);
mDialog.create();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
final Window w = mDialog.getWindow();
- // QS is not closed when pressing CastTile. Match the type of the dialog shown from the
- // tile.
- w.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mDialog.show();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index c3b4354ebabe..66c036cee600 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -17,6 +17,9 @@
package com.android.systemui.media.dagger;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.MediaTttReceiverLogBuffer;
+import com.android.systemui.log.dagger.MediaTttSenderLogBuffer;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
@@ -27,8 +30,11 @@ import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger;
import java.util.Optional;
@@ -112,6 +118,24 @@ public interface MediaModule {
return Optional.of(controllerReceiverLazy.get());
}
+ @Provides
+ @SysUISingleton
+ @MediaTttSenderLogger
+ static MediaTttLogger providesMediaTttSenderLogger(
+ @MediaTttSenderLogBuffer LogBuffer buffer
+ ) {
+ return new MediaTttLogger("Sender", buffer);
+ }
+
+ @Provides
+ @SysUISingleton
+ @MediaTttReceiverLogger
+ static MediaTttLogger providesMediaTttReceiverLogger(
+ @MediaTttReceiverLogBuffer LogBuffer buffer
+ ) {
+ return new MediaTttLogger("Receiver", buffer);
+ }
+
/** */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 3cd390533132..0b23ad50a726 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -54,6 +54,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
MediaOutputDialog mediaOutputDialog) {
super(controller);
mMediaOutputDialog = mediaOutputDialog;
+ setHasStableIds(true);
}
@Override
@@ -79,6 +80,20 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
@Override
+ public long getItemId(int position) {
+ final int size = mController.getMediaDevices().size();
+ if (position == size && mController.isZeroMode()) {
+ return -1;
+ } else if (position < size) {
+ return ((List<MediaDevice>) (mController.getMediaDevices()))
+ .get(position).getId().hashCode();
+ } else if (DEBUG) {
+ Log.d(TAG, "Incorrect position for item id: " + position);
+ }
+ return position;
+ }
+
+ @Override
public int getItemCount() {
if (mController.isZeroMode()) {
// Add extra one for "pair new" or dynamic group
@@ -159,7 +174,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
onCheckBoxClicked(false, device);
});
setCheckBoxColor(mCheckBox, mController.getColorActiveItem());
- initSessionSeekbar();
+ initSeekbar(device);
} else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
mStatusIcon.setImageDrawable(
mContext.getDrawable(R.drawable.media_output_status_check));
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 1f11d0c62bd7..c96aca37987b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -249,7 +249,7 @@ public abstract class MediaOutputBaseAdapter extends
mSeekBar.setMin(0);
final int currentVolume = device.getCurrentVolume();
if (mSeekBar.getProgress() != currentVolume) {
- mSeekBar.setProgress(currentVolume);
+ mSeekBar.setProgress(currentVolume, true);
}
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
@@ -278,7 +278,7 @@ public abstract class MediaOutputBaseAdapter extends
mSeekBar.setMin(0);
final int currentVolume = mController.getSessionVolume();
if (mSeekBar.getProgress() != currentVolume) {
- mSeekBar.setProgress(currentVolume);
+ mSeekBar.setProgress(currentVolume, true);
}
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 15b8f13ee0f8..9c4b39d9cb77 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -43,6 +43,7 @@ import com.android.systemui.util.view.ViewUtil
*/
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
internal val context: Context,
+ internal val logger: MediaTttLogger,
private val windowManager: WindowManager,
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
@@ -93,18 +94,26 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
// Cancel and re-set the chip timeout each time we get a new state.
cancelChipViewTimeout?.run()
- cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
+ cancelChipViewTimeout = mainExecutor.executeDelayed(
+ { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
+ chipState.getTimeoutMs()
+ )
}
- /** Hides the chip. */
- fun removeChip() {
- // TODO(b/203800347): We may not want to hide the chip if we're currently in a
- // TransferTriggered state: Once the user has initiated the transfer, they should be able
- // to move away from the receiver device but still see the status of the transfer.
+ /**
+ * Hides the chip.
+ *
+ * @param removalReason a short string describing why the chip was removed (timeout, state
+ * change, etc.)
+ */
+ open fun removeChip(removalReason: String) {
if (chipView == null) { return }
+ logger.logChipRemoval(removalReason)
tapGestureDetector.removeOnGestureDetectedCallback(TAG)
windowManager.removeView(chipView)
chipView = null
+ // No need to time the chip out since it's already gone
+ cancelChipViewTimeout?.run()
}
/**
@@ -136,7 +145,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
// If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the
// chip is tappable).
if (!viewUtil.touchIsWithinView(view, e.x, e.y)) {
- removeChip()
+ removeChip(MediaTttRemovalReason.REASON_SCREEN_TAP)
}
}
}
@@ -145,5 +154,9 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
private val TAG = MediaTttChipControllerCommon::class.simpleName!!
-@VisibleForTesting
-const val TIMEOUT_MILLIS = 3000L
+
+object MediaTttRemovalReason {
+ const val REASON_TIMEOUT = "TIMEOUT"
+ const val REASON_SCREEN_TAP = "SCREEN_TAP"
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
index 2da48cef792a..6f6018170f98 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
@@ -52,6 +52,14 @@ open class MediaTttChipState(
null
}
}
+
+ /**
+ * Returns the amount of time this chip should display on the screen before it times out and
+ * disappears. [MediaTttChipControllerCommon] will ensure that the timeout resets each time we
+ * receive a new state.
+ */
+ open fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
}
+private const val DEFAULT_TIMEOUT_MILLIS = 3000L
private val TAG = MediaTttChipState::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
new file mode 100644
index 000000000000..d3b5bc62b0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.common
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.MediaTttSenderLogBuffer
+
+/**
+ * A logger for media tap-to-transfer events.
+ *
+ * @property deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
+ */
+class MediaTttLogger(
+ private val deviceTypeTag: String,
+ @MediaTttSenderLogBuffer private val buffer: LogBuffer
+){
+ /** Logs a change in the chip state for the given [mediaRouteId]. */
+ fun logStateChange(stateName: String, mediaRouteId: String) {
+ buffer.log(
+ BASE_TAG + deviceTypeTag,
+ LogLevel.DEBUG,
+ {
+ str1 = stateName
+ str2 = mediaRouteId
+ },
+ { "State changed to $str1 for ID=$str2" }
+ )
+ }
+
+ /** Logs that we removed the chip for the given [reason]. */
+ fun logChipRemoval(reason: String) {
+ buffer.log(
+ BASE_TAG + deviceTypeTag,
+ LogLevel.DEBUG,
+ { str1 = reason },
+ { "Chip removed due to $str1" }
+ )
+ }
+}
+
+private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 3d43ebe81742..1a96ddf69b28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -28,6 +28,7 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -43,6 +44,7 @@ import javax.inject.Inject
class MediaTttChipControllerReceiver @Inject constructor(
commandQueue: CommandQueue,
context: Context,
+ @MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
@@ -50,6 +52,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
@Main private val mainHandler: Handler,
) : MediaTttChipControllerCommon<ChipStateReceiver>(
context,
+ logger,
windowManager,
viewUtil,
mainExecutor,
@@ -79,6 +82,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
appIcon: Icon?,
appName: CharSequence?
) {
+ logger.logStateChange(stateIntToString(displayState), routeInfo.id)
when(displayState) {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> {
val packageName = routeInfo.packageName
@@ -97,7 +101,8 @@ class MediaTttChipControllerReceiver @Inject constructor(
)
}
}
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> removeChip()
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER ->
+ removeChip(removalReason = FAR_FROM_SENDER)
else ->
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
}
@@ -106,6 +111,16 @@ class MediaTttChipControllerReceiver @Inject constructor(
override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) {
setIcon(chipState, currentChipView)
}
+
+ private fun stateIntToString(@StatusBarManager.MediaTransferReceiverState state: Int): String {
+ return when (state) {
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> CLOSE_TO_SENDER
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> FAR_FROM_SENDER
+ else -> "INVALID: $state"
+ }
+ }
}
private const val RECEIVER_TAG = "MediaTapToTransferRcvr"
+private const val CLOSE_TO_SENDER = "CLOSE_TO_SENDER"
+private const val FAR_FROM_SENDER = "FAR_FROM_SENDER"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
new file mode 100644
index 000000000000..54fc48ddba91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.media.taptotransfer.receiver
+
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import javax.inject.Qualifier
+
+@Qualifier
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+annotation class MediaTttReceiverLogger
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 9b537fb48ba9..22424a4e9c74 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -97,6 +97,7 @@ class TransferToReceiverTriggered(
}
override fun showLoading() = true
+ override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
}
/**
@@ -111,6 +112,7 @@ class TransferToThisDeviceTriggered(
}
override fun showLoading() = true
+ override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
}
/**
@@ -194,3 +196,8 @@ class TransferFailed(
return context.getString(R.string.media_transfer_failed)
}
}
+
+// Give the Transfer*Triggered states a longer timeout since those states represent an active
+// process and we should keep the user informed about it as long as possible (but don't allow it to
+// continue indefinitely).
+private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 180e4ee9726c..da2aac4d5ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -29,6 +29,8 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
+import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -43,13 +45,22 @@ import javax.inject.Inject
class MediaTttChipControllerSender @Inject constructor(
commandQueue: CommandQueue,
context: Context,
+ @MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<ChipStateSender>(
- context, windowManager, viewUtil, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
+ context,
+ logger,
+ windowManager,
+ viewUtil,
+ mainExecutor,
+ tapGestureDetector,
+ R.layout.media_ttt_chip
) {
+ private var currentlyDisplayedChipState: ChipStateSender? = null
+
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
@@ -71,6 +82,7 @@ class MediaTttChipControllerSender @Inject constructor(
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?
) {
+ logger.logStateChange(stateIntToString(displayState), routeInfo.id)
val appPackageName = routeInfo.packageName
val otherDeviceName = routeInfo.name.toString()
val chipState = when(displayState) {
@@ -90,7 +102,7 @@ class MediaTttChipControllerSender @Inject constructor(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
TransferFailed(appPackageName)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER -> {
- removeChip()
+ removeChip(removalReason = FAR_FROM_RECEIVER)
null
}
else -> {
@@ -106,6 +118,8 @@ class MediaTttChipControllerSender @Inject constructor(
/** Displays the chip view for the given state. */
override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) {
+ currentlyDisplayedChipState = chipState
+
// App icon
setIcon(chipState, currentChipView)
@@ -129,6 +143,43 @@ class MediaTttChipControllerSender @Inject constructor(
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
if (showFailure) { View.VISIBLE } else { View.GONE }
}
+
+ override fun removeChip(removalReason: String) {
+ // Don't remove the chip if we're mid-transfer since the user should still be able to
+ // see the status of the transfer. (But do remove it if it's finally timed out.)
+ if ((currentlyDisplayedChipState is TransferToReceiverTriggered ||
+ currentlyDisplayedChipState is TransferToThisDeviceTriggered)
+ && removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
+ return
+ }
+ super.removeChip(removalReason)
+ currentlyDisplayedChipState = null
+ }
+
+ private fun stateIntToString(@StatusBarManager.MediaTransferSenderState state: Int): String {
+ return when(state) {
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
+ "ALMOST_CLOSE_TO_START_CAST"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
+ "ALMOST_CLOSE_TO_END_CAST"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
+ "TRANSFER_TO_RECEIVER_TRIGGERED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
+ "TRANSFER_TO_THIS_DEVICE_TRIGGERED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
+ "TRANSFER_TO_RECEIVER_SUCCEEDED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
+ "TRANSFER_TO_THIS_DEVICE_SUCCEEDED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED ->
+ "TRANSFER_TO_RECEIVER_FAILED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
+ "TRANSFER_TO_THIS_DEVICE_FAILED"
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER ->
+ FAR_FROM_RECEIVER
+ else -> "INVALID: $state"
+ }
+ }
}
const val SENDER_TAG = "MediaTapToTransferSender"
+private const val FAR_FROM_RECEIVER = "FAR_FROM_RECEIVER"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
new file mode 100644
index 000000000000..4393af9a99ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.media.taptotransfer.sender
+
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import javax.inject.Qualifier
+
+@Qualifier
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+annotation class MediaTttSenderLogger
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 5e9edb7c906e..a1a319814269 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -49,8 +49,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -79,7 +78,7 @@ public final class NavBarHelper implements
Dumpable {
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -113,7 +112,7 @@ public final class NavBarHelper implements
SystemActions systemActions,
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -121,7 +120,7 @@ public final class NavBarHelper implements
mContentResolver = mContext.getContentResolver();
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(
@@ -295,8 +294,8 @@ public final class NavBarHelper implements
* {@link InputMethodService} and the keyguard states.
*/
public boolean isImeShown(int vis) {
- View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
- boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+ View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+ boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 76914f3fd8ee..ec6094dd9973 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -53,8 +53,8 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
-import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions;
import android.annotation.IdRes;
import android.app.ActivityTaskManager;
@@ -134,9 +134,9 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -176,7 +176,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final SysUiState mSysUiFlagsContainer;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
@@ -486,7 +486,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -509,7 +509,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mSysUiFlagsContainer = sysUiFlagsContainer;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
@@ -611,7 +611,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
public void onViewAttachedToWindow(View v) {
final Display display = v.getDisplay();
mNavigationBarView.setComponents(mRecentsOptional);
- mNavigationBarView.setComponents(mStatusBarOptionalLazy.get().get().getPanelController());
+ mNavigationBarView.setComponents(mCentralSurfacesOptionalLazy.get().get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
@@ -757,7 +757,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
- mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
+ | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
mWindowManager.addView(mOrientationHandle, mOrientationParams);
mOrientationHandle.setVisibility(View.GONE);
mOrientationParams.setFitInsetsTypes(0 /* types*/);
@@ -1165,13 +1166,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallUI at this point,
// and their ONLY options are to answer or reject the call.)
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
if (mTelecomManagerOptional.isPresent()
&& mTelecomManagerOptional.get().isRinging()) {
- if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ if (centralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing)
+ .orElse(false)) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
mHomeBlockedThisTouch = true;
@@ -1187,14 +1189,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
- statusBarOptional.ifPresent(StatusBar::awakenDreams);
+ centralSurfacesOptional.ifPresent(CentralSurfaces::awakenDreams);
break;
}
return false;
}
private void onVerticalChanged(boolean isVertical) {
- mStatusBarOptionalLazy.get().ifPresent(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
statusBar -> statusBar.setQsScrimEnabled(!isVertical));
}
@@ -1221,7 +1223,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
AssistManager.INVOCATION_TYPE_KEY,
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mAssistManagerLazy.get().startAssist(args);
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
mNavigationBarView.abortCurrentGesture();
return true;
}
@@ -1247,7 +1249,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
mCommandQueue.toggleRecentApps();
}
@@ -1431,7 +1433,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private void checkBarModes() {
// We only have status bar on default display now.
if (mIsOnDefaultDisplay) {
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::checkBarModes);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::checkBarModes);
} else {
checkNavBarModes();
}
@@ -1450,7 +1452,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
*/
public void checkNavBarModes() {
final boolean anim =
- mStatusBarOptionalLazy.get().map(StatusBar::isDeviceInteractive).orElse(false)
+ mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
+ .orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
}
@@ -1563,7 +1566,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
lp.token = new Binder();
lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC
+ | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
lp.windowAnimations = 0;
lp.setTitle("NavigationBar" + mContext.getDisplayId());
@@ -1626,7 +1630,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final CommandQueue mCommandQueue;
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationShadeDepthController mNotificationShadeDepthController;
@@ -1657,7 +1661,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -1685,7 +1689,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mCommandQueue = commandQueue;
mPipOptional = pipOptional;
mRecentsOptional = recentsOptional;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationShadeDepthController = notificationShadeDepthController;
@@ -1710,7 +1714,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mOverviewProxyService, mNavigationModeController,
mAccessibilityButtonModeObserver, mStatusBarStateController,
mSysUiFlagsContainer, mBroadcastDispatcher, mCommandQueue, mPipOptional,
- mRecentsOptional, mStatusBarOptionalLazy,
+ mRecentsOptional, mCentralSurfacesOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mMainHandler,
mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index ede10a5f2f18..017bbdffdc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -90,7 +90,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -1362,7 +1362,7 @@ public class NavigationBarView extends FrameLayout implements
getContextDisplay().getRealSize(size);
pw.println("NavigationBarView:");
- pw.println(String.format(" this: " + StatusBar.viewInfo(this)
+ pw.println(String.format(" this: " + CentralSurfaces.viewInfo(this)
+ " " + visibilityToString(getVisibility())));
getWindowVisibleDisplayFrame(r);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9ea27634df6a..4dacf5dfd78c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -263,6 +263,15 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
// Notify FalsingManager that an intentional gesture has occurred.
// TODO(b/186519446): use a different method than isFalseTouch
mFalsingManager.isFalseTouch(BACK_GESTURE);
+ // Only inject back keycodes when ahead-of-time back dispatching is disabled.
+ if (mBackAnimation == null) {
+ boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
+ + sendDown + ", up=" + sendUp);
+ }
+ }
mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
@@ -936,6 +945,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
public void setBackAnimation(BackAnimation backAnimation) {
mBackAnimation = backAnimation;
+ if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
+ ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index a6bad15e0865..a6919e826d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -280,7 +280,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
}
};
private BackCallback mBackCallback;
- private final BackAnimation mBackAnimation;
+ private BackAnimation mBackAnimation;
public NavigationBarEdgePanel(Context context,
BackAnimation backAnimation) {
@@ -385,6 +385,10 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
mShowProtection = !isPrimaryDisplay;
}
+ public void setBackAnimation(BackAnimation backAnimation) {
+ mBackAnimation = backAnimation;
+ }
+
@Override
public void onDestroy() {
cancelFailsafe();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index dbd641bffe7e..039c33315741 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -118,6 +118,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String ACTION_AUTO_SAVER_NO_THANKS =
"PNW.autoSaverNoThanks";
+ private static final String ACTION_ENABLE_SEVERE_BATTERY_DIALOG = "PNW.enableSevereDialog";
+
private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
"android.settings.BATTERY_SAVER_SETTINGS";
public static final String BATTERY_SAVER_SCHEDULE_SCREEN_INTENT_ACTION =
@@ -253,20 +255,25 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
protected void showWarningNotification() {
- final String percentage = NumberFormat.getPercentInstance()
- .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);
-
- // get shared standard notification copy
- String title = mContext.getString(R.string.battery_low_title);
- String contentText;
-
- // get correct content text if notification is hybrid or not
- if (mCurrentBatterySnapshot.isHybrid()) {
- contentText = getHybridContentString(percentage);
- } else {
- contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
+ if (showSevereLowBatteryDialog()) {
+ mContext.sendBroadcast(new Intent(ACTION_ENABLE_SEVERE_BATTERY_DIALOG)
+ .setPackage(mContext.getPackageName())
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND));
+ // Reset the state once dialog been enabled
+ dismissLowBatteryNotification();
+ mPlaySound = false;
+ return;
}
+ final int warningLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ final String percentage = NumberFormat.getPercentInstance()
+ .format((double) warningLevel / 100.0);
+ final String title = mContext.getString(R.string.battery_low_title);
+ final String contentText = mContext.getString(
+ R.string.battery_low_description, percentage);
+
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.BATTERY)
.setSmallIcon(R.drawable.ic_power_low)
@@ -284,7 +291,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
// Make the notification red if the percentage goes below a certain amount or the time
// remaining estimate is disabled
- if (!mCurrentBatterySnapshot.isHybrid() || mBucket < 0
+ if (!mCurrentBatterySnapshot.isHybrid() || mBucket < -1
|| mCurrentBatterySnapshot.getTimeRemainingMillis()
< mCurrentBatterySnapshot.getSevereThresholdMillis()) {
nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
@@ -303,6 +310,13 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
+ private boolean showSevereLowBatteryDialog() {
+ final boolean isSevereState = !mCurrentBatterySnapshot.isHybrid() || mBucket < -1;
+ final boolean useSevereDialog = mContext.getResources().getBoolean(
+ R.bool.config_severe_battery_dialog);
+ return isSevereState && useSevereDialog;
+ }
+
private void showAutoSaverSuggestionNotification() {
final CharSequence message = mContext.getString(R.string.auto_saver_text);
final Notification.Builder nb =
@@ -662,8 +676,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
// If there's no link, use the string with no "learn more".
if (TextUtils.isEmpty(learnMoreUrl)) {
- return mContext.getText(
- com.android.internal.R.string.battery_saver_description);
+ return mContext.getText(R.string.battery_low_intro);
}
// If we have a link, use the string with the "learn more" link.
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 642af593c211..56528c9974ac 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -47,7 +47,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,7 +69,7 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
- private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
+ private static final int CHARGE_CYCLE_PERCENT_RESET = 30;
private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
public static final int NO_ESTIMATE_AVAILABLE = -1;
private static final String BOOT_COUNT_KEY = "boot_count";
@@ -108,17 +108,17 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
private IThermalEventListener mUsbThermalEventListener;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue, Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
PowerManager powerManager) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mWarnings = warningsUI;
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
@@ -206,7 +206,8 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
- * less than 0 means that the battery is low
+ * less than 0 means that the battery is low, -1 means the battery is reaching warning level,
+ * -2 means the battery is reaching severe level.
*/
private int findBatteryLevelBucket(int level) {
if (level >= mLowBatteryAlertCloseLevel) {
@@ -388,12 +389,8 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
@VisibleForTesting
void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
- // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
- // notification again
- final long timeRemainingMillis = currentSnapshot.getTimeRemainingMillis();
- if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
- && (timeRemainingMillis > SIX_HOURS_MILLIS
- || timeRemainingMillis == NO_ESTIMATE_AVAILABLE)) {
+ // if we are now over 30% battery, we can trigger hybrid notification again
+ if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET) {
mLowWarningShownThisChargeCycle = false;
mSevereWarningShownThisChargeCycle = false;
if (DEBUG) {
@@ -403,6 +400,7 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
|| lastSnapshot.getPlugged();
+ final long timeRemainingMillis = currentSnapshot.getTimeRemainingMillis();
if (shouldShowHybridWarning(currentSnapshot)) {
mWarnings.showLowBatteryWarning(playSound);
@@ -444,19 +442,13 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
return false;
}
- final long timeRemainingMillis = snapshot.getTimeRemainingMillis();
// Only show the low warning if enabled once per charge cycle & no battery saver
- final boolean canShowWarning = snapshot.isLowWarningEnabled()
- && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
- && ((timeRemainingMillis != NO_ESTIMATE_AVAILABLE
- && timeRemainingMillis < snapshot.getLowThresholdMillis())
- || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
+ final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
+ && snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold();
// Only show the severe warning once per charge cycle
final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
- && ((timeRemainingMillis != NO_ESTIMATE_AVAILABLE
- && timeRemainingMillis < snapshot.getSevereThresholdMillis())
- || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
+ && snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold();
final boolean canShow = canShowWarning || canShowSevereWarning;
@@ -712,8 +704,10 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (!statusBarOptional.map(StatusBar::isDeviceInVrMode).orElse(false)) {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (!centralSurfacesOptional.map(CentralSurfaces::isDeviceInVrMode)
+ .orElse(false)) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 89735c3a547d..5d9361d201c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -370,6 +370,7 @@ class FgsManagerController @Inject constructor(
PowerExemptionManager.REASON_SYSTEM_UID,
PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
+ PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE,
PowerExemptionManager.REASON_DEVICE_OWNER,
PowerExemptionManager.REASON_PROFILE_OWNER,
PowerExemptionManager.REASON_PROC_STATE_PERSISTENT,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 8afb7933aacc..95b4b725e4c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -118,7 +118,7 @@ class HeaderPrivacyIconsController @Inject constructor(
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
if (safetyCenterEnabled) {
- showSafetyHub()
+ showSafetyCenter()
} else {
privacyDialogController.showDialog(privacyChip.context)
}
@@ -131,16 +131,16 @@ class HeaderPrivacyIconsController @Inject constructor(
updatePrivacyIconSlots()
}
- private fun showSafetyHub() {
+ private fun showSafetyCenter() {
backgroundExecutor.execute {
val usage = ArrayList(permGroupUsage())
privacyLogger.logUnfilteredPermGroupUsage(usage)
- val startSafetyHub = Intent(Intent.ACTION_VIEW_SAFETY_HUB)
- startSafetyHub.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
+ val startSafetyCenter = Intent(Intent.ACTION_VIEW_SAFETY_CENTER_QS)
+ startSafetyCenter.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
usage)
- startSafetyHub.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK
uiExecutor.execute {
- activityStarter.startActivity(startSafetyHub, true,
+ activityStarter.startActivity(startSafetyCenter, true,
ActivityLaunchAnimator.Controller.fromView(privacyChip))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index c69307548b6e..47af7de9118d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -53,7 +53,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -102,7 +102,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final StatusBarIconController mIconController;
private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
private int mCurrentUser;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private Context mUserContext;
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
@@ -121,7 +121,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
Provider<AutoTileManager> autoTiles,
DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
QSLogger qsLogger,
UiEventLogger uiEventLogger,
UserTracker userTracker,
@@ -143,7 +143,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
@@ -227,17 +227,17 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
@Override
public void collapsePanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateCollapsePanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateCollapsePanels);
}
@Override
public void forceCollapsePanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateForceCollapsePanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateForceCollapsePanels);
}
@Override
public void openPanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateOpenPanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateOpenPanels);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 32e0805b91ba..a49d3fd16591 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -204,6 +204,9 @@ public class TileLifecycleManager extends BroadcastReceiver implements
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
| Context.BIND_WAIVE_PRIORITY,
mUser);
+ if (!mIsBound) {
+ mContext.unbindService(this);
+ }
} catch (SecurityException e) {
Log.e(TAG, "Failed to bind to service", e);
mIsBound = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 597f7b7053a2..f389df097d7d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -26,7 +26,7 @@ import android.util.Log;
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -42,7 +42,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private Context mContext;
private Handler mHandler;
@@ -51,8 +51,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ public OverviewProxyRecentsImpl(Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
@@ -109,9 +109,10 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
}
};
// Preload only if device for current user is unlocked
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
- statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) {
+ centralSurfacesOptional.get().executeRunnableDismissingKeyguard(() -> {
mHandler.post(toggleRecents);
}, null, true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 7ceaee5fb7bb..a3dea1c68b14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -102,7 +102,7 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.wm.shell.back.BackAnimation;
@@ -146,7 +146,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Context mContext;
private final Optional<Pip> mPipOptional;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Optional<SplitScreen> mSplitScreenOptional;
private SysUiState mSysUiState;
private final Handler mHandler;
@@ -186,7 +186,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void startScreenPinning(int taskId) {
verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
- mStatusBarOptionalLazy.get().ifPresent(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
statusBar -> statusBar.showScreenPinningRequest(taskId,
false /* allowCancel */)));
}
@@ -207,9 +207,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
public void onStatusBarMotionEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
// TODO move this logic to message queue
- mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
+ mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
if (event.getActionMasked() == ACTION_DOWN) {
- statusBar.getPanelController().startExpandLatencyTracking();
+ centralSurfaces.getPanelController().startExpandLatencyTracking();
}
mHandler.post(() -> {
int action = event.getActionMasked();
@@ -217,7 +217,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mInputFocusTransferStarted = true;
mInputFocusTransferStartY = event.getY();
mInputFocusTransferStartMillis = event.getEventTime();
- statusBar.onInputFocusTransfer(
+ centralSurfaces.onInputFocusTransfer(
mInputFocusTransferStarted, false /* cancel */,
0 /* velocity */);
}
@@ -225,7 +225,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mInputFocusTransferStarted = false;
float velocity = (event.getY() - mInputFocusTransferStartY)
/ (event.getEventTime() - mInputFocusTransferStartMillis);
- statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
+ centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
action == ACTION_CANCEL,
velocity);
}
@@ -401,7 +401,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void toggleNotificationPanel() {
verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () ->
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::togglePanel));
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::togglePanel));
}
@@ -555,7 +555,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
Optional<Pip> pipOptional,
@@ -573,7 +573,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
mStatusBarWinController = statusBarWinController;
@@ -665,7 +665,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
final NavigationBarView navBarView =
mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
final NotificationPanelViewController panelController =
- mStatusBarOptionalLazy.get().get().getPanelController();
+ mCentralSurfacesOptionalLazy.get().get().getPanelController();
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView + " panelController=" + panelController);
@@ -731,9 +731,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
+ mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
mInputFocusTransferStarted = false;
- statusBar.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
+ centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
});
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 7f130cb203c0..15ad7798b3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -56,7 +56,7 @@ import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
@@ -70,7 +70,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
NavigationModeController.ModeChangedListener {
private final Context mContext;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
@@ -83,9 +83,11 @@ public class ScreenPinningRequest implements View.OnClickListener,
private int taskId;
@Inject
- public ScreenPinningRequest(Context context, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ public ScreenPinningRequest(
+ Context context,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mWindowManager = (WindowManager)
@@ -267,9 +269,10 @@ public class ScreenPinningRequest implements View.OnClickListener,
.setVisibility(View.INVISIBLE);
}
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
NavigationBarView navigationBarView =
- statusBarOptional.map(StatusBar::getNavigationBarView).orElse(null);
+ centralSurfacesOptional.map(CentralSurfaces::getNavigationBarView).orElse(null);
final boolean recentsVisible = navigationBarView != null
&& navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index f14044682b61..daaa897374cb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -24,7 +24,7 @@ import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
import android.app.ActivityOptions;
import android.app.PendingIntent;
@@ -36,7 +36,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -49,15 +49,15 @@ import javax.inject.Inject;
public class ActionProxyReceiver extends BroadcastReceiver {
private static final String TAG = "ActionProxyReceiver";
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final ScreenshotSmartActions mScreenshotSmartActions;
@Inject
- public ActionProxyReceiver(Optional<StatusBar> statusBar,
+ public ActionProxyReceiver(Optional<CentralSurfaces> centralSurfacesOptional,
ActivityManagerWrapper activityManagerWrapper,
ScreenshotSmartActions screenshotSmartActions) {
- mStatusBar = statusBar.orElse(null);
+ mCentralSurfaces = centralSurfacesOptional.orElse(null);
mActivityManagerWrapper = activityManagerWrapper;
mScreenshotSmartActions = screenshotSmartActions;
}
@@ -89,8 +89,8 @@ public class ActionProxyReceiver extends BroadcastReceiver {
};
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ if (mCentralSurfaces != null) {
+ mCentralSurfaces.executeRunnableDismissingKeyguard(startActivityRunnable, null,
true /* dismissShade */, true /* afterKeyguardGone */,
true /* deferred */);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 991a68fb056a..7801c68586f1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -54,7 +54,7 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import javax.inject.Inject;
public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
- private static final String TAG = "StatusBar.BrightnessController";
+ private static final String TAG = "CentralSurfaces.BrightnessController";
private static final int SLIDER_ANIMATION_DURATION = 3000;
private static final int MSG_UPDATE_SLIDER = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 645c5accb541..5932a64c1c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -23,7 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.ONLY_CORE_APPS;
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
@@ -310,11 +310,11 @@ public class CommandQueue extends IStatusBar.Stub implements
long requestId, @BiometricMultiSensorMode int multiSensorConfig) {
}
- /** @see IStatusBar#onBiometricAuthenticated() */
- default void onBiometricAuthenticated() {
+ /** @see IStatusBar#onBiometricAuthenticated(int) */
+ default void onBiometricAuthenticated(@Modality int modality) {
}
- /** @see IStatusBar#onBiometricHelp(String) */
+ /** @see IStatusBar#onBiometricHelp(int, String) */
default void onBiometricHelp(@Modality int modality, String message) {
}
@@ -963,9 +963,11 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
- public void onBiometricAuthenticated() {
+ public void onBiometricAuthenticated(@Modality int modality) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = modality;
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
}
}
@@ -1465,9 +1467,11 @@ public class CommandQueue extends IStatusBar.Stub implements
break;
}
case MSG_BIOMETRIC_AUTHENTICATED: {
+ SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onBiometricAuthenticated();
+ mCallbacks.get(i).onBiometricAuthenticated(someArgs.argi1 /* modality */);
}
+ someArgs.recycle();
break;
}
case MSG_BIOMETRIC_HELP: {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 8a9d6ddb2e14..17f42b1a3a43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
import java.io.FileDescriptor
@@ -73,7 +73,7 @@ class LockscreenShadeTransitionController @Inject constructor(
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
lateinit var notificationPanelController: NotificationPanelViewController
- lateinit var statusbar: StatusBar
+ lateinit var centralSurfaces: CentralSurfaces
lateinit var qS: QS
/**
@@ -197,7 +197,7 @@ class LockscreenShadeTransitionController @Inject constructor(
// Bind the click listener of the shelf to go to the full shade
notificationShelfController.setOnClickListener {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
- statusbar.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
+ centralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
goToLockedShade(it)
}
}
@@ -224,7 +224,7 @@ class LockscreenShadeTransitionController @Inject constructor(
if (nsslController.isInLockedDownShade()) {
logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
- statusbar.dismissKeyguardThenExecute(OnDismissAction {
+ centralSurfaces.dismissKeyguardThenExecute(OnDismissAction {
nextHideKeyguardNeedsNoAnimation = true
false
}, cancelRunnable, false /* afterKeyguardGone */)
@@ -361,7 +361,7 @@ class LockscreenShadeTransitionController @Inject constructor(
notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
depthController.transitionToFullShadeProgress = scrimProgress
udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
- statusbar.setTransitionToFullShadeProgress(scrimProgress)
+ centralSurfaces.setTransitionToFullShadeProgress(scrimProgress)
}
private fun setDragDownAmountAnimated(
@@ -461,7 +461,7 @@ class LockscreenShadeTransitionController @Inject constructor(
animationHandler: ((Long) -> Unit)? = null,
cancelAction: Runnable? = null
) {
- if (statusbar.isShadeDisabled) {
+ if (centralSurfaces.isShadeDisabled) {
cancelAction?.run()
logger.logShadeDisabledOnGoToLockedShade()
return
@@ -503,7 +503,7 @@ class LockscreenShadeTransitionController @Inject constructor(
cancelAction?.run()
}
logger.logShowBouncerOnGoToLockedShade()
- statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
+ centralSurfaces.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
draggedDownEntry = entry
} else {
logger.logGoingToLockedShade(animationHandler != null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 01bdb401e00c..68d35f9679ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -32,10 +32,10 @@ import android.util.Log;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.time.SystemClock;
import java.util.ArrayList;
@@ -54,7 +54,7 @@ import javax.inject.Inject;
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
- private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean DEBUG = CentralSurfaces.DEBUG;
private static final long MAX_RANKING_DELAY_MILLIS = 500L;
private final Context mContext;
@@ -69,7 +69,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {
private long mSkippingRankingUpdatesSince = -1;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
@Inject
public NotificationListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 3730d123021b..052c57e768fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
-import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
-import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_MEDIA_FAKE_ARTWORK;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.ENABLE_LOCKSCREEN_WALLPAPER;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -55,7 +55,7 @@ import com.android.systemui.media.MediaData;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.SmartspaceMediaData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -66,11 +66,11 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -131,7 +131,7 @@ public class NotificationMediaManager implements Dumpable {
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -172,11 +172,11 @@ public class NotificationMediaManager implements Dumpable {
};
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public NotificationMediaManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
@@ -193,7 +193,7 @@ public class NotificationMediaManager implements Dumpable {
mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
@@ -575,7 +575,7 @@ public class NotificationMediaManager implements Dumpable {
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- Trace.beginSection("StatusBar#updateMediaMetaData");
+ Trace.beginSection("CentralSurfaces#updateMediaMetaData");
if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
Trace.endSection();
return;
@@ -653,7 +653,8 @@ public class NotificationMediaManager implements Dumpable {
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
boolean hideBecauseOccluded =
- mStatusBarOptionalLazy.get().map(StatusBar::isOccluded).orElse(false);
+ mCentralSurfacesOptionalLazy.get()
+ .map(CentralSurfaces::isOccluded).orElse(false);
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 17bf346fa22b..3b3b5a2d05f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,7 +19,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
- * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
+ * An abstraction of something that presents notifications, e.g. CentralSurfaces. Contains methods
* for both querying the state of the system (some modularised piece of functionality may
* want to act differently based on e.g. whether the presenter is visible to the user or not) and
* for affecting the state of the system (e.g. starting an intent, given that the presenter may
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2b5453a56434..94a6d3e99842 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -55,7 +55,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -64,7 +64,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry.
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
@@ -103,7 +103,7 @@ public class NotificationRemoteInputManager implements Dumpable {
private final Handler mMainHandler;
private final ActionClickLogger mLogger;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
protected final Context mContext;
protected final NotifPipelineFlags mNotifPipelineFlags;
@@ -125,8 +125,8 @@ public class NotificationRemoteInputManager implements Dumpable {
@Override
public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.wakeUpIfDozing(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"));
final NotificationEntry entry = getNotificationForParent(view.getParent());
@@ -253,7 +253,7 @@ public class NotificationRemoteInputManager implements Dumpable {
};
/**
- * Injected constructor. See {@link StatusBarDependenciesModule}.
+ * Injected constructor. See {@link CentralSurfacesDependenciesModule}.
*/
public NotificationRemoteInputManager(
Context context,
@@ -263,7 +263,7 @@ public class NotificationRemoteInputManager implements Dumpable {
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -276,7 +276,7 @@ public class NotificationRemoteInputManager implements Dumpable {
mSmartReplyController = smartReplyController;
mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
index 4f70fdb0d978..3b1fa1779c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import android.view.View;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -25,7 +26,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
import javax.inject.Inject;
@@ -114,8 +114,8 @@ public class NotificationShelfController {
return mView.getIntrinsicHeight();
}
- public void setOnActivatedListener(StatusBarNotificationPresenter presenter) {
- mView.setOnActivatedListener(presenter);
+ public void setOnActivatedListener(ActivatableNotificationView.OnActivatedListener listener) {
+ mView.setOnActivatedListener(listener);
}
public void setOnClickListener(View.OnClickListener onClickListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 092e86daddb8..054543c7d2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -32,7 +32,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -45,7 +45,6 @@ import com.android.systemui.statusbar.notification.collection.legacy.VisualStabi
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -97,7 +96,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private final Optional<Bubbles> mBubblesOptional;
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
- private final ForegroundServiceSectionController mFgsSectionController;
private final NotifPipelineFlags mNotifPipelineFlags;
private AssistantFeedbackController mAssistantFeedbackController;
private final KeyguardStateController mKeyguardStateController;
@@ -115,7 +113,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private boolean mIsHandleDynamicPrivacyChangeScheduled;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public NotificationViewHierarchyManager(
Context context,
@@ -129,7 +127,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
KeyguardBypassController bypassController,
Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
@@ -145,7 +142,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mEntryManager = notificationEntryManager;
- mFgsSectionController = fgsSectionController;
mNotifPipelineFlags = notifPipelineFlags;
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
@@ -417,8 +413,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
ent.getKey(), ent.getSbn().getGroupKey());
if (ent.isRowDismissed() || ent.isRowRemoved()
- || isBubbleNotificationSuppressedFromShade
- || mFgsSectionController.hasEntry(ent)) {
+ || isBubbleNotificationSuppressedFromShade) {
// we want to suppress removed notifications because they could
// temporarily become children if they were isolated before.
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
index 2e1762a26b7e..78077386179a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
@@ -20,17 +20,17 @@ import android.view.View;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
/**
* Calculates and moves the QS frame vertically.
*/
public abstract class QsFrameTranslateController {
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
- public QsFrameTranslateController(StatusBar statusBar) {
- mStatusBar = statusBar;
+ public QsFrameTranslateController(CentralSurfaces centralSurfaces) {
+ mCentralSurfaces = centralSurfaces;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
index c15679709c3d..33e224579bef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
@@ -21,7 +21,7 @@ import android.view.View;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import javax.inject.Inject;
@@ -32,8 +32,8 @@ import javax.inject.Inject;
public class QsFrameTranslateImpl extends QsFrameTranslateController {
@Inject
- public QsFrameTranslateImpl(StatusBar statusBar) {
- super(statusBar);
+ public QsFrameTranslateImpl(CentralSurfaces centralSurfaces) {
+ super(centralSurfaces);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 4ad01aab98a7..058edda20971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -25,7 +25,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -45,7 +45,7 @@ public class SmartReplyController implements Dumpable {
private Callback mCallback;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public SmartReplyController(
DumpManager dumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 02870a382056..2763bd711338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -420,8 +420,9 @@ public class StatusBarStateControllerImpl implements
* notified before unranked, and we will sort ranked listeners from low to high
*
* @deprecated This method exists only to solve latent inter-dependencies from refactoring
- * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
- * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ * StatusBarState out of CentralSurfaces.java. Any new listeners should be built not to need
+ * ranking (i.e., they are non-dependent on the order of operations of StatusBarState
+ * listeners).
*/
@Deprecated
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index f0b2c2d54dbe..2b3190159ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -25,7 +25,7 @@ import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.lang.annotation.Retention;
@@ -51,8 +51,9 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
* notified before unranked, and we will sort ranked listeners from low to high
*
* @deprecated This method exists only to solve latent inter-dependencies from refactoring
- * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
- * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ * StatusBarState out of CentralSurfaces.java. Any new listeners should be built not to need
+ * ranking (i.e., they are non-dependent on the order of operations of StatusBarState
+ * listeners).
*/
@Deprecated
void addCallback(StateListener listener, int rank);
@@ -91,7 +92,7 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
int getCurrentOrUpcomingState();
/**
- * Update the dozing state from {@link StatusBar}'s perspective
+ * Update the dozing state from {@link CentralSurfaces}'s perspective
* @param isDozing well, are we dozing?
* @return {@code true} if the state changed, else {@code false}
*/
@@ -116,7 +117,7 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
/**
- * Update the expanded state from {@link StatusBar}'s perspective
+ * Update the expanded state from {@link CentralSurfaces}'s perspective
* @param expanded are we expanded?
* @return {@code true} if the state changed, else {@code false}
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index fe5a69996eb7..41d2b655d805 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -843,7 +843,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
}
}
- /** Box for StatusBar icon info */
+ /** Box for status bar icon info */
private static final class SbInfo {
final boolean showTriangle;
final int ratTypeIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index d65fa3acfa37..a62a152ab7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -21,18 +21,18 @@ import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.window.StatusBarWindowController
import java.lang.IllegalStateException
import javax.inject.Inject
/**
- * Responsible for creating the StatusBar window and initializing the root components of that window
- * (see [CollapsedStatusBarFragment])
+ * Responsible for creating the status bar window and initializing the root components of that
+ * window (see [CollapsedStatusBarFragment])
*/
-@StatusBarScope
+@CentralSurfacesScope
class StatusBarInitializer @Inject constructor(
private val windowController: StatusBarWindowController
) {
@@ -43,7 +43,7 @@ class StatusBarInitializer @Inject constructor(
* Creates the status bar window and root views, and initializes the component
*/
fun initializeStatusBar(
- sbComponent: StatusBarComponent
+ centralSurfacesComponent: CentralSurfacesComponent
) {
windowController.fragmentHostManager.addTagListener(
CollapsedStatusBarFragment.TAG,
@@ -64,7 +64,7 @@ class StatusBarInitializer @Inject constructor(
}).fragmentManager
.beginTransaction()
.replace(R.id.status_bar_container,
- sbComponent.createCollapsedStatusBarFragment(),
+ centralSurfacesComponent.createCollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index fa6b502776db..83290af24f2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -60,11 +60,10 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
@@ -88,12 +87,13 @@ import dagger.Module;
import dagger.Provides;
/**
- * This module provides instances needed to construct {@link StatusBar}. These are moved to this
- * separate from {@link StatusBarModule} module so that components that wish to build their own
- * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ * This module provides instances needed to construct {@link CentralSurfaces}. These are moved to
+ * this separate from {@link CentralSurfacesModule} module so that components that wish to build
+ * their own version of CentralSurfaces can include just dependencies, without injecting
+ * CentralSurfaces itself.
*/
@Module
-public interface StatusBarDependenciesModule {
+public interface CentralSurfacesDependenciesModule {
/** */
@SysUISingleton
@Provides
@@ -105,7 +105,7 @@ public interface StatusBarDependenciesModule {
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -120,7 +120,7 @@ public interface StatusBarDependenciesModule {
visibilityProvider,
notificationEntryManager,
rebuilder,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
@@ -134,7 +134,7 @@ public interface StatusBarDependenciesModule {
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
@@ -148,7 +148,7 @@ public interface StatusBarDependenciesModule {
DumpManager dumpManager) {
return new NotificationMediaManager(
context,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
notificationShadeWindowController,
visibilityProvider,
notificationEntryManager,
@@ -198,7 +198,6 @@ public interface StatusBarDependenciesModule {
KeyguardBypassController bypassController,
Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
@@ -217,7 +216,6 @@ public interface StatusBarDependenciesModule {
bypassController,
bubblesOptional,
privacyController,
- fgsSectionController,
dynamicChildBindController,
lowPriorityInflationHelper,
assistantFeedbackController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
index ad5ef2043ccd..99d4b2e525d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
@@ -23,7 +23,7 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import dagger.Module;
/** */
-@Module(includes = {StatusBarPhoneModule.class, StatusBarDependenciesModule.class,
+@Module(includes = {StatusBarPhoneModule.class, CentralSurfacesDependenciesModule.class,
NotificationsModule.class, NotificationRowModule.class})
-public interface StatusBarModule {
+public interface CentralSurfacesModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
index 46c1abb859b3..fe55dea7333a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
@@ -17,17 +17,17 @@
package com.android.systemui.statusbar.dagger
import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
-interface StartStatusBarModule {
- /** Start the StatusBar */
+interface StartCentralSurfacesModule {
+ /** Start the CentralSurfaces */
@Binds
@IntoMap
- @ClassKey(StatusBar::class)
- abstract fun bindsStatusBar(statusBar: StatusBar): CoreStartable
-} \ No newline at end of file
+ @ClassKey(CentralSurfaces::class)
+ abstract fun bindsCentralSurfaces(centralSurfaces: CentralSurfaces): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
deleted file mode 100644
index 314051c8ce6c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification
-
-import android.content.Context
-import android.provider.DeviceConfig
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.util.DeviceConfigProxy
-import javax.inject.Inject
-
-private var sIsEnabled: Boolean? = null
-
-/**
- * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config.
- */
-// TODO: this is really boilerplatey, make a base class that just wraps the device config
-@SysUISingleton
-class ForegroundServiceDismissalFeatureController @Inject constructor(
- val proxy: DeviceConfigProxy,
- val context: Context
-) {
- fun isForegroundServiceDismissalEnabled(): Boolean {
- return isEnabled(proxy)
- }
-}
-
-private fun isEnabled(proxy: DeviceConfigProxy): Boolean {
- if (sIsEnabled == null) {
- sIsEnabled = proxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_ALLOW_FGS_DISMISSAL, false)
- }
-
- return sIsEnabled!!
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index da706215863e..392145ad306a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -24,7 +24,7 @@ import android.view.View;
import com.android.systemui.DejankUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
@@ -39,7 +39,7 @@ public final class NotificationClicker implements View.OnClickListener {
private static final String TAG = "NotificationClicker";
private final NotificationClickerLogger mLogger;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private final Optional<Bubbles> mBubblesOptional;
private final NotificationActivityStarter mNotificationActivityStarter;
@@ -53,11 +53,11 @@ public final class NotificationClicker implements View.OnClickListener {
private NotificationClicker(
NotificationClickerLogger logger,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter) {
mLogger = logger;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mBubblesOptional = bubblesOptional;
mNotificationActivityStarter = notificationActivityStarter;
}
@@ -69,7 +69,7 @@ public final class NotificationClicker implements View.OnClickListener {
return;
}
- mStatusBarOptional.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
+ mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"));
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -137,13 +137,13 @@ public final class NotificationClicker implements View.OnClickListener {
/** Builds an instance. */
public NotificationClicker build(
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter
) {
return new NotificationClicker(
mLogger,
- statusBarOptional,
+ centralSurfacesOptional,
bubblesOptional,
notificationActivityStarter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f97b936605f6..ac5beece880e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -111,7 +111,6 @@ public class NotificationEntryManager implements
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
private final LeakDetector mLeakDetector;
- private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private final IStatusBarService mStatusBarService;
private final NotifLiveDataStoreImpl mNotifLiveDataStore;
private final DumpManager mDumpManager;
@@ -159,7 +158,6 @@ public class NotificationEntryManager implements
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager
@@ -170,7 +168,6 @@ public class NotificationEntryManager implements
mNotificationRowBinderLazy = notificationRowBinderLazy;
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
- mFgsFeatureController = fgsFeatureController;
mStatusBarService = statusBarService;
mNotifLiveDataStore = notifLiveDataStore;
mDumpManager = dumpManager;
@@ -958,7 +955,7 @@ public class NotificationEntryManager implements
Trace.endSection();
}
- /** dump the current active notification list. Called from StatusBar */
+ /** dump the current active notification list. Called from CentralSurfaces */
public void dump(PrintWriter pw, String indent) {
pw.println("NotificationEntryManager (Legacy)");
int filteredLen = mSortedAndFiltered.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 9da7d21886ab..2c1296f34a42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -8,12 +8,15 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.policy.HeadsUpUtil
+import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.max
/** A provider of [NotificationLaunchAnimatorController]. */
-class NotificationLaunchAnimatorControllerProvider(
+@CentralSurfacesComponent.CentralSurfacesScope
+class NotificationLaunchAnimatorControllerProvider @Inject constructor(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
index 5dc0dcc4d717..c71eade79cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
@@ -23,7 +23,7 @@ import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
@SysUISingleton
@@ -39,7 +39,7 @@ class TargetSdkResolver @Inject constructor(
}
private fun resolveNotificationSdk(sbn: StatusBarNotification): Int {
- val pmUser = StatusBar.getPackageManagerForUser(context, sbn.user.identifier)
+ val pmUser = CentralSurfaces.getPackageManagerForUser(context, sbn.user.identifier)
var targetSdk = 0
// Extract target SDK version.
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
index 4ee08ed4899f..bdbb0eb48e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.legacy;
-import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SPEW;
import android.service.notification.StatusBarNotification;
import android.util.Log;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
index 920d3c4cbe55..470737e3b772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
@@ -17,8 +17,8 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.NotificationPanelViewController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.util.ListenerSet
import dagger.Binds
import dagger.Module
@@ -67,18 +67,18 @@ object StatusBarNotifPanelEventSourceModule {
@JvmStatic
@Provides
@IntoSet
- @StatusBarScope
+ @CentralSurfacesScope
fun bindStartable(
manager: NotifPanelEventSourceManager,
notifPanelController: NotificationPanelViewController
- ): StatusBarComponent.Startable =
+ ): CentralSurfacesComponent.Startable =
EventSourceStatusBarStartableImpl(manager, notifPanelController)
}
/**
- * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that
- * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves
- * come from [NotificationPanelViewController] in [StatusBarScope].
+ * Management layer that bridges [SysUiSingleton] and [CentralSurfacesScope]. Necessary because code
+ * that wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events
+ * themselves come from [NotificationPanelViewController] in [CentralSurfacesScope].
*/
interface NotifPanelEventSourceManager : NotifPanelEventSource {
var eventSource: NotifPanelEventSource?
@@ -116,7 +116,7 @@ private class NotifPanelEventSourceManagerImpl
private class EventSourceStatusBarStartableImpl(
private val manager: NotifPanelEventSourceManager,
private val notifPanelController: NotificationPanelViewController
-) : StatusBarComponent.Startable {
+) : CentralSurfacesComponent.Startable {
override fun start() {
manager.eventSource = notifPanelController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index e3ebef99f45f..51bbf1c80478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -38,7 +38,6 @@ import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
@@ -87,7 +86,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager;
@@ -95,6 +94,8 @@ import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Provider;
+
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -128,7 +129,6 @@ public interface NotificationsModule {
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager) {
@@ -139,7 +139,6 @@ public interface NotificationsModule {
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
- fgsFeatureController,
statusBarService,
notifLiveDataStore,
dumpManager);
@@ -150,7 +149,7 @@ public interface NotificationsModule {
@Provides
static NotificationGutsManager provideNotificationGutsManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -170,7 +169,7 @@ public interface NotificationsModule {
DumpManager dumpManager) {
return new NotificationGutsManager(
context,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
mainHandler,
bgHandler,
accessibilityManager,
@@ -279,8 +278,8 @@ public interface NotificationsModule {
@Provides
static NotificationsController provideNotificationsController(
Context context,
- Lazy<NotificationsControllerImpl> realController,
- Lazy<NotificationsControllerStub> stubController) {
+ Provider<NotificationsControllerImpl> realController,
+ Provider<NotificationsControllerStub> stubController) {
if (context.getResources().getBoolean(R.bool.config_renderNotifications)) {
return realController.get();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index a59d4211e68a..18abfcaab0e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -23,11 +23,8 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.Optional
/**
* The master controller for all notifications-related work
@@ -37,8 +34,6 @@ import java.util.Optional
*/
interface NotificationsController {
fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 48f2dafedcbb..98f45fa070c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinde
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.RemoteInputUriController
@@ -64,6 +64,7 @@ import javax.inject.Inject
*/
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
+ private val centralSurfaces: Lazy<CentralSurfaces>,
private val notifPipelineFlags: NotifPipelineFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
@@ -86,12 +87,11 @@ class NotificationsControllerImpl @Inject constructor(
private val headsUpViewBinder: HeadsUpViewBinder,
private val clickerBuilder: NotificationClicker.Builder,
private val animatedImageNotificationManager: AnimatedImageNotificationManager,
- private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
+ private val bubblesOptional: Optional<Bubbles>,
) : NotificationsController {
override fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
@@ -109,7 +109,7 @@ class NotificationsControllerImpl @Inject constructor(
notificationRowBinder.setNotificationClicker(
clickerBuilder.build(
- Optional.of(statusBar), bubblesOptional, notificationActivityStarter))
+ Optional.of(centralSurfaces.get()), bubblesOptional, notificationActivityStarter))
notificationRowBinder.setUpWithPresenter(
presenter,
listContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 1c9af11b7816..66701d1e37f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -24,11 +24,8 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.Optional
import javax.inject.Inject
/**
@@ -39,8 +36,6 @@ class NotificationsControllerStub @Inject constructor(
) : NotificationsController {
override fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
@@ -75,4 +70,4 @@ class NotificationsControllerStub @Inject constructor(
pw.println("Notification handling disabled")
pw.println()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index dc3941332f0b..9fbd5c39dedd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -468,7 +468,7 @@ public class NotificationLogger implements StateListener {
}
/**
- * Called by StatusBar to notify the logger that the panel expansion has changed.
+ * Called by CentralSurfaces to notify the logger that the panel expansion has changed.
* The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
* @param isExpanded True if the panel is expanded.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
deleted file mode 100644
index dbfa27f1f68e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Copyright (C) 2020 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.systemui.statusbar.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-
-class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
- var entry: NotificationEntry? = null
- set(value) {
- field = value
- update()
- }
-
- private fun update() {
- (findViewById(R.id.app_name) as TextView).apply {
- text = entry?.row?.appName
- }
-
- (findViewById(R.id.icon) as StatusBarIconView).apply {
- set(entry?.icons?.statusBarIcon?.statusBarIcon)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1f7d93012e39..c237e1deeae3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -112,7 +112,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
@@ -388,7 +388,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
return false;
} else {
- PackageManager packageManager = StatusBar.getPackageManagerForUser(
+ PackageManager packageManager = CentralSurfaces.getPackageManagerForUser(
context, sbn.getUser().getIdentifier());
Boolean isSystemNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
deleted file mode 100644
index 17396ad31ba2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-
-import com.android.systemui.R
-
-class ForegroundServiceDungeonView(context: Context, attrs: AttributeSet)
- : StackScrollerDecorView(context, attrs) {
- override fun findContentView(): View? {
- return findViewById(R.id.foreground_service_dungeon)
- }
-
- override fun findSecondaryView(): View? {
- return null
- }
-
- override fun setVisible(visible: Boolean, animate: Boolean) {
- // Visibility is controlled by the ForegroundServiceSectionController
- }
-} \ No newline at end of file
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 1530e5238c67..4c693045bc88 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
@@ -47,7 +47,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
@@ -841,7 +841,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
StatusBarNotification sbn = mEntry.getSbn();
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
- Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+ Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
if (mCallback != null) {
mCallback.handleInflationException(mRow.getEntry(),
new InflationException("Couldn't inflate contentViews" + e));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6d13024e5489..ebe6f03c44e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -70,8 +70,8 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
@@ -120,7 +120,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -139,7 +139,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
* Injected constructor. See {@link NotificationsModule}.
*/
public NotificationGutsManager(Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -158,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
ShadeController shadeController,
DumpManager dumpManager) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mAccessibilityManager = accessibilityManager;
@@ -342,7 +342,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
}
StatusBarNotification sbn = row.getEntry().getSbn();
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
@@ -363,7 +363,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
(View v, Intent intent) -> {
@@ -416,7 +416,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
if (!userHandle.equals(UserHandle.ALL)
@@ -458,7 +458,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// Settings link is only valid for notifications that specify a non-system user
NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
final NotificationConversationInfo.OnAppSettingsClickListener onAppSettingsClick =
(View v, Intent intent) -> {
@@ -571,11 +571,12 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
.setLeaveOpenOnKeyguardHide(true);
}
- Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.isPresent()) {
+ Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.isPresent()) {
Runnable r = () -> mMainHandler.post(
() -> openGutsInternal(view, x, y, menuItem));
- statusBarOptional.get().executeRunnableDismissingKeyguard(
+ centralSurfacesOptional.get().executeRunnableDismissingKeyguard(
r,
null /* cancelAction */,
false /* dismissShade */,
@@ -584,7 +585,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
return true;
}
/**
- * When {@link StatusBar} doesn't exist, falling through to call
+ * When {@link CentralSurfaces} doesn't exist, falling through to call
* {@link #openGutsInternal(View,int,int,NotificationMenuRowPlugin.MenuItem)}.
*/
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index a12d0073ef57..1a7417a78186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -26,7 +26,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import dagger.Binds;
import dagger.BindsInstance;
@@ -100,7 +100,7 @@ public interface ExpandableNotificationRowComponent {
// but since this field is used in the guts, it must be accurate.
// Therefore we will only show the application label, or, failing that, the
// package name. No substitutions.
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
context, statusBarNotification.getUser().getIdentifier());
final String pkg = statusBarNotification.getPackageName();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
index 4555b839a3f2..fa14123df11f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Retention;
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
deleted file mode 100644
index 75ca3377fa89..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.stack
-
-import android.content.Context
-import android.service.notification.NotificationListenerService.REASON_CANCEL
-import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
-import android.service.notification.NotificationListenerService.REASON_CLICK
-import android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController
-import com.android.systemui.statusbar.notification.NotificationEntryListener
-import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.DungeonRow
-import com.android.systemui.util.Assert
-import javax.inject.Inject
-
-/**
- * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground
- * service notifications and can reinstantiate them when requested.
- */
-@SysUISingleton
-class ForegroundServiceSectionController @Inject constructor(
- val entryManager: NotificationEntryManager,
- val featureController: ForegroundServiceDismissalFeatureController
-) {
- private val TAG = "FgsSectionController"
- private var context: Context? = null
-
- private val entries = mutableSetOf<NotificationEntry>()
-
- private var entriesView: View? = null
-
- init {
- if (featureController.isForegroundServiceDismissalEnabled()) {
- entryManager.addNotificationRemoveInterceptor(this::shouldInterceptRemoval)
-
- entryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onPostEntryUpdated(entry: NotificationEntry) {
- if (entries.contains(entry)) {
- removeEntry(entry)
- addEntry(entry)
- update()
- }
- }
- })
- }
- }
-
- private fun shouldInterceptRemoval(
- key: String,
- entry: NotificationEntry?,
- reason: Int
- ): Boolean {
- Assert.isMainThread()
- val isClearAll = reason == REASON_CANCEL_ALL
- val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
- // REASON_APP_CANCEL and REASON_APP_CANCEL_ALL are ignored, because the
- // foreground service associated with it is gone.
- val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
-
- if (entry == null) return false
-
- // We only want to retain notifications that the user dismissed
- // TODO: centralize the entry.isClearable logic and this so that it's clear when a notif is
- // clearable
- if (isUserDismiss && !entry.sbn.isClearable) {
- if (!hasEntry(entry)) {
- addEntry(entry)
- update()
- }
- // TODO: This isn't ideal. Slightly better would at least be to have NEM update the
- // notif list when an entry gets intercepted
- entryManager.updateNotifications(
- "FgsSectionController.onNotificationRemoveRequested")
- return true
- } else if ((isClearAll || isSummaryCancel) && !entry.sbn.isClearable) {
- // In the case where a FGS notification is part of a group that is cleared or a clear
- // all, we actually want to stop its removal but also not put it into the dungeon
- return true
- } else if (hasEntry(entry)) {
- removeEntry(entry)
- update()
- return false
- }
-
- return false
- }
-
- private fun removeEntry(entry: NotificationEntry) {
- Assert.isMainThread()
- entries.remove(entry)
- }
-
- private fun addEntry(entry: NotificationEntry) {
- Assert.isMainThread()
- entries.add(entry)
- }
-
- fun hasEntry(entry: NotificationEntry): Boolean {
- Assert.isMainThread()
- return entries.contains(entry)
- }
-
- fun initialize(context: Context) {
- this.context = context
- }
-
- fun createView(li: LayoutInflater): View {
- entriesView = li.inflate(R.layout.foreground_service_dungeon, null)
- // Start out gone
- entriesView!!.visibility = View.GONE
- return entriesView!!
- }
-
- private fun update() {
- Assert.isMainThread()
- if (entriesView == null) {
- throw IllegalStateException("ForegroundServiceSectionController is trying to show " +
- "dismissed fgs notifications without having been initialized!")
- }
-
- // TODO: these views should be recycled and not inflating on the main thread
- (entriesView!!.findViewById(R.id.entry_list) as LinearLayout).apply {
- removeAllViews()
- entries.sortedBy { it.ranking.rank }.forEach { entry ->
- val child = LayoutInflater.from(context)
- .inflate(R.layout.foreground_service_dungeon_row, null) as DungeonRow
-
- child.entry = entry
- child.setOnClickListener {
- removeEntry(child.entry!!)
- update()
- entry.row.unDismiss()
- entry.row.resetTranslation()
- entryManager.updateNotifications("ForegroundServiceSectionController.onClick")
- }
-
- addView(child)
- }
- }
-
- if (entries.isEmpty()) {
- entriesView?.visibility = View.GONE
- } else {
- entriesView?.visibility = View.VISIBLE
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2c4db7745fd4..efe559a64cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -100,13 +100,12 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
@@ -309,7 +308,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
private NotificationStackScrollLogger mLogger;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
@@ -453,7 +452,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private final NotificationSectionsManager mSectionsManager;
- private ForegroundServiceDungeonView mFgsSectionView;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
private float mLastSentExpandedHeight;
@@ -614,14 +612,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
- void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
- if (mFgsSectionView != null) {
- return;
- }
- mFgsSectionView = fgsSectionView;
- addView(mFgsSectionView, -1);
- }
-
/**
* Set the overexpansion of the panel to be applied to the view.
*/
@@ -3969,7 +3959,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
resetScrollPosition();
- mStatusBar.resetUserExpandedStates();
+ mCentralSurfaces.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
if (mSwipeHelper.isSwiping()) {
@@ -4601,8 +4591,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setStatusBar(StatusBar statusBar) {
- this.mStatusBar = statusBar;
+ public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
+ this.mCentralSurfaces = centralSurfaces;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -5270,7 +5260,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
Intent intent = showHistory
? new Intent(Settings.ACTION_NOTIFICATION_HISTORY)
: new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
- mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ mCentralSurfaces.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
});
setEmptyShadeView(view);
}
@@ -5286,9 +5276,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// incremented in the following "changeViewPosition" calls so that its value is correct for
// subsequent calls.
int offsetFromEnd = 1;
- if (mFgsSectionView != null) {
- changeViewPosition(mFgsSectionView, getChildCount() - offsetFromEnd++);
- }
changeViewPosition(mFooterView, getChildCount() - offsetFromEnd++);
changeViewPosition(mEmptyShadeView, getChildCount() - offsetFromEnd++);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d1c63e3ade70..df6b8f59fc36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -86,7 +86,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -109,7 +108,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
@@ -119,8 +117,8 @@ import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -143,7 +141,7 @@ import kotlin.Unit;
/**
* Controller for {@link NotificationStackScrollLayout}.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -170,8 +168,6 @@ public class NotificationStackScrollLayoutController {
private final NotificationEntryManager mNotificationEntryManager;
private final IStatusBarService mIStatusBarService;
private final UiEventLogger mUiEventLogger;
- private final ForegroundServiceDismissalFeatureController mFgFeatureController;
- private final ForegroundServiceSectionController mFgServicesSectionController;
private final LayoutInflater mLayoutInflater;
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisualStabilityManager mVisualStabilityManager;
@@ -180,8 +176,8 @@ public class NotificationStackScrollLayoutController {
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final NotificationLockscreenUserManager mLockscreenUserManager;
- // TODO: StatusBar should be encapsulated behind a Controller
- private final StatusBar mStatusBar;
+ // TODO: CentralSurfaces should be encapsulated behind a Controller
+ private final CentralSurfaces mCentralSurfaces;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
@@ -334,7 +330,7 @@ public class NotificationStackScrollLayoutController {
mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
mLockscreenUserManager.isAnyProfilePublicMode());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
- mNotificationEntryManager.updateNotifications("StatusBar state changed");
+ mNotificationEntryManager.updateNotifications("CentralSurfaces state changed");
}
};
@@ -435,7 +431,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
- mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
+ mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption);
}
@Override
@@ -488,7 +484,7 @@ public class NotificationStackScrollLayoutController {
mView.addSwipedOutView(view);
mFalsingCollector.onNotificationDismissed();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
null,
null /* cancelAction */,
false /* dismissShade */,
@@ -561,7 +557,7 @@ public class NotificationStackScrollLayoutController {
@Override
public float getFalsingThresholdFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -648,7 +644,7 @@ public class NotificationStackScrollLayoutController {
FalsingManager falsingManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
ScrimController scrimController,
NotificationGroupManagerLegacy legacyGroupManager,
GroupExpansionManager groupManager,
@@ -660,8 +656,6 @@ public class NotificationStackScrollLayoutController {
LockscreenShadeTransitionController lockscreenShadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
- ForegroundServiceDismissalFeatureController fgFeatureController,
- ForegroundServiceSectionController fgServicesSectionController,
LayoutInflater layoutInflater,
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
@@ -691,7 +685,7 @@ public class NotificationStackScrollLayoutController {
mFalsingManager = falsingManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mScrimController = scrimController;
mJankMonitor = jankMonitor;
groupManager.registerGroupExpansionChangeListener(
@@ -699,7 +693,7 @@ public class NotificationStackScrollLayoutController {
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@Override
public void onGroupsChanged() {
- mStatusBar.requestNotificationUpdate("onGroupsChanged");
+ mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
}
});
mNotifPipelineFlags = notifPipelineFlags;
@@ -709,8 +703,6 @@ public class NotificationStackScrollLayoutController {
mNotificationEntryManager = notificationEntryManager;
mIStatusBarService = iStatusBarService;
mUiEventLogger = uiEventLogger;
- mFgFeatureController = fgFeatureController;
- mFgServicesSectionController = fgServicesSectionController;
mLayoutInflater = layoutInflater;
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
@@ -724,7 +716,7 @@ public class NotificationStackScrollLayoutController {
mView.setController(this);
mView.setLogger(mLogger);
mView.setTouchHandler(new TouchHandler());
- mView.setStatusBar(mStatusBar);
+ mView.setCentralSurfaces(mCentralSurfaces);
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
@@ -744,12 +736,6 @@ public class NotificationStackScrollLayoutController {
mNotificationRoundnessManager.setShouldRoundPulsingViews(
!mKeyguardBypassController.getBypassEnabled());
- if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
- mView.initializeForegroundServiceSection(
- (ForegroundServiceDungeonView) mFgServicesSectionController.createView(
- mLayoutInflater));
- }
-
mSwipeHelper = mNotificationSwipeHelperBuilder
.setSwipeDirection(SwipeHelper.X)
.setNotificationCallback(mNotificationCallback)
@@ -1420,7 +1406,7 @@ public class NotificationStackScrollLayoutController {
return mNotificationRoundnessManager;
}
- public NotificationListContainer getNotificationListContainer() {
+ NotificationListContainer getNotificationListContainer() {
return mNotificationListContainer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
new file mode 100644
index 000000000000..3dcaae28e181
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public abstract class NotificationStackScrollLayoutListContainerModule {
+ @Provides
+ @CentralSurfacesComponent.CentralSurfacesScope
+ static NotificationListContainer provideListContainer(
+ NotificationStackScrollLayoutController nsslController) {
+ return nsslController.getNotificationListContainer();
+ }
+}
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 d5d1ceada88a..fe637c14ee33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -717,7 +717,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
public void run() {
mNotificationShadeWindowController.setForceDozeBrightness(false);
}
- }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
+ }, CentralSurfaces.FADE_KEYGUARD_DURATION_PULSING);
}
public void finishKeyguardFadingAway() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 66b6eaf9c1fb..4235c1f6b0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -222,7 +222,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -260,8 +260,20 @@ import javax.inject.Named;
import dagger.Lazy;
-/** */
-public class StatusBar extends CoreStartable implements
+/**
+ * A class handling initialization and coordination between some of the key central surfaces in
+ * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
+ *
+ * This class is not our ideal architecture because it doesn't enforce much isolation between these
+ * three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would
+ * break it up into three modules -- one for each of those three surfaces -- and we would define any
+ * APIs that are needed for these surfaces to communicate with each other when necessary.
+ *
+ * <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is
+ * to break up this class into many small classes, and any code added here will slow down that goal.
+ * </b>
+ */
+public class CentralSurfaces extends CoreStartable implements
ActivityStarter,
LifecycleOwner {
public static final boolean MULTIUSER_DEBUG = false;
@@ -278,7 +290,7 @@ public class StatusBar extends CoreStartable implements
"com.android.systemui.statusbar.banner_action_cancel";
private static final String BANNER_ACTION_SETUP =
"com.android.systemui.statusbar.banner_action_setup";
- public static final String TAG = "StatusBar";
+ public static final String TAG = "CentralSurfaces";
public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
@@ -343,8 +355,9 @@ public class StatusBar extends CoreStartable implements
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final DreamOverlayStateController mDreamOverlayStateController;
- private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+ private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
+ private NotificationListContainer mNotifListContainer;
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
updateBubblesVisibility();
@@ -468,7 +481,7 @@ public class StatusBar extends CoreStartable implements
private PhoneStatusBarTransitions mStatusBarTransitions;
private AuthRippleController mAuthRippleController;
@WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
- protected NotificationShadeWindowController mNotificationShadeWindowController;
+ protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
@@ -494,10 +507,8 @@ public class StatusBar extends CoreStartable implements
protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final DozeParameters mDozeParameters;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
- private final StatusBarComponent.Factory mStatusBarComponentFactory;
+ private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
private final PluginManager mPluginManager;
- private final StatusBarNotificationActivityStarter.Builder
- mStatusBarNotificationActivityStarterBuilder;
private final ShadeController mShadeController;
private final InitController mInitController;
@@ -543,7 +554,7 @@ public class StatusBar extends CoreStartable implements
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
- private StatusBarComponent mStatusBarComponent;
+ private CentralSurfacesComponent mCentralSurfacesComponent;
// Flags for disabling the status bar
// Two variables becaseu the first one evidently ran out of room for new flags.
@@ -665,7 +676,7 @@ public class StatusBar extends CoreStartable implements
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
- protected StatusBarNotificationPresenter mPresenter;
+ protected NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -684,13 +695,13 @@ public class StatusBar extends CoreStartable implements
/**
- * Public constructor for StatusBar.
+ * Public constructor for CentralSurfaces.
*
- * StatusBar is considered optional, and therefore can not be marked as @Inject directly.
+ * CentralSurfaces is considered optional, and therefore can not be marked as @Inject directly.
* Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- public StatusBar(
+ public CentralSurfaces(
Context context,
NotificationsController notificationsController,
FragmentService fragmentService,
@@ -750,10 +761,8 @@ public class StatusBar extends CoreStartable implements
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- StatusBarComponent.Factory statusBarComponentFactory,
+ CentralSurfacesComponent.Factory centralSurfacesComponentFactory,
PluginManager pluginManager,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
@@ -848,9 +857,8 @@ public class StatusBar extends CoreStartable implements
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
- mStatusBarComponentFactory = statusBarComponentFactory;
+ mCentralSurfacesComponentFactory = centralSurfacesComponentFactory;
mPluginManager = pluginManager;
- mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
@@ -879,7 +887,7 @@ public class StatusBar extends CoreStartable implements
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
mNotifPipelineFlags = notifPipelineFlags;
- lockscreenShadeTransitionController.setStatusbar(this);
+ lockscreenShadeTransitionController.setCentralSurfaces(this);
statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
mScreenOffAnimationController = screenOffAnimationController;
@@ -1111,7 +1119,7 @@ public class StatusBar extends CoreStartable implements
}
private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
- Trace.beginSection("StatusBar#onFoldedStateChanged");
+ Trace.beginSection("CentralSurfaces#onFoldedStateChanged");
onFoldedStateChangedInternal(isFolded, willGoToSleep);
Trace.endSection();
}
@@ -1155,19 +1163,14 @@ public class StatusBar extends CoreStartable implements
updateTheme();
inflateStatusBarWindow();
- mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
mWallpaperController.setRootView(mNotificationShadeWindowView);
- // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
+ // TODO: Deal with the ugliness that comes from having some of the status bar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- NotificationListContainer notifListContainer =
- mStackScrollerController.getNotificationListContainer();
- mNotificationLogger.setUpWithContainer(notifListContainer);
-
+ mNotificationLogger.setUpWithContainer(mNotifListContainer);
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
-
mUserSwitcherController.init(mNotificationShadeWindowView);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1175,7 +1178,7 @@ public class StatusBar extends CoreStartable implements
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
- StatusBarInitializer initializer = mStatusBarComponent.getStatusBarInitializer();
+ StatusBarInitializer initializer = mCentralSurfacesComponent.getStatusBarInitializer();
initializer.setStatusBarViewUpdatedListener(
(statusBarView, statusBarViewController, statusBarTransitions) -> {
mStatusBarView = statusBarView;
@@ -1192,7 +1195,7 @@ public class StatusBar extends CoreStartable implements
setBouncerShowingForStatusBarComponents(mBouncerShowing);
checkBarModes();
});
- initializer.initializeStatusBar(mStatusBarComponent);
+ initializer.initializeStatusBar(mCentralSurfacesComponent);
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
@@ -1442,65 +1445,19 @@ public class StatusBar extends CoreStartable implements
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
- mStackScrollerController.getNotificationListContainer(),
- mHeadsUpManager,
- mJankMonitor
- );
-
- // TODO: inject this.
- mPresenter = new StatusBarNotificationPresenter(
- mContext,
- mNotificationPanelViewController,
+ mNotifListContainer,
mHeadsUpManager,
- mNotificationShadeWindowView,
- mStackScrollerController,
- mDozeScrimController,
- mScrimController,
- mNotificationShadeWindowController,
- mDynamicPrivacyController,
- mKeyguardStateController,
- mKeyguardIndicationController,
- this /* statusBar */,
- mShadeController,
- mLockscreenShadeTransitionController,
- mCommandQueue,
- mViewHierarchyManager,
- mLockscreenUserManager,
- mStatusBarStateController,
- mNotifShadeEventSource,
- mEntryManager,
- mMediaManager,
- mGutsManager,
- mKeyguardUpdateMonitor,
- mLockscreenGestureLogger,
- mInitController,
- mNotificationInterruptStateProvider,
- mRemoteInputManager,
- mConfigurationController,
- mNotifPipelineFlags);
-
+ mJankMonitor);
mNotificationShelfController.setOnActivatedListener(mPresenter);
mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
-
- mNotificationActivityStarter =
- mStatusBarNotificationActivityStarterBuilder
- .setStatusBar(this)
- .setActivityLaunchAnimator(mActivityLaunchAnimator)
- .setNotificationAnimatorControllerProvider(mNotificationAnimationProvider)
- .setNotificationPresenter(mPresenter)
- .setNotificationPanelViewController(mNotificationPanelViewController)
- .build();
mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
-
mNotificationsController.initialize(
- this,
- mBubblesOptional,
mPresenter,
- mStackScrollerController.getNotificationListContainer(),
+ mNotifListContainer,
mStackScrollerController.getNotifStackController(),
mNotificationActivityStarter,
- mPresenter);
+ mCentralSurfacesComponent.getBindRowCallback());
}
/**
@@ -1558,30 +1515,34 @@ public class StatusBar extends CoreStartable implements
}
private void inflateStatusBarWindow() {
- if (mStatusBarComponent != null) {
+ if (mCentralSurfacesComponent != null) {
// Tear down
- for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
- startable.stop();
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.stop();
}
}
- mStatusBarComponent = mStatusBarComponentFactory.create();
- mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
+ mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
+ mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
- mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView();
- mNotificationShadeWindowViewController = mStatusBarComponent
+ mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
+ mNotificationShadeWindowViewController = mCentralSurfacesComponent
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- mNotificationPanelViewController = mStatusBarComponent.getNotificationPanelViewController();
- mStatusBarComponent.getLockIconViewController().init();
- mStackScrollerController = mStatusBarComponent.getNotificationStackScrollLayoutController();
+ mNotificationPanelViewController =
+ mCentralSurfacesComponent.getNotificationPanelViewController();
+ mCentralSurfacesComponent.getLockIconViewController().init();
+ mStackScrollerController =
+ mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
mStackScroller = mStackScrollerController.getView();
-
- mNotificationShelfController = mStatusBarComponent.getNotificationShelfController();
- mAuthRippleController = mStatusBarComponent.getAuthRippleController();
+ mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
+ mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
+ mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
+ mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
+ mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController();
mAuthRippleController.init();
- mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
+ mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
// Listen for demo mode changes
mDemoModeController.addCallback(mDemoModeCallback);
@@ -1589,18 +1550,19 @@ public class StatusBar extends CoreStartable implements
if (mCommandQueueCallbacks != null) {
mCommandQueue.removeCallback(mCommandQueueCallbacks);
}
- mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
+ mCommandQueueCallbacks =
+ mCentralSurfacesComponent.getCentralSurfacesCommandQueueCallbacks();
// Connect in to the status bar manager service
mCommandQueue.addCallback(mCommandQueueCallbacks);
- // Perform all other initialization for StatusBarScope
- for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
- startable.start();
+ // Perform all other initialization for CentralSurfacesScope
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.start();
}
}
protected void startKeyguard() {
- Trace.beginSection("StatusBar#startKeyguard");
+ Trace.beginSection("CentralSurfaces#startKeyguard");
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
mBiometricUnlockController.setBiometricModeListener(
new BiometricUnlockController.BiometricModeListener() {
@@ -1621,7 +1583,7 @@ public class StatusBar extends CoreStartable implements
@Override
public void notifyBiometricAuthModeChanged() {
- StatusBar.this.notifyBiometricAuthModeChanged();
+ CentralSurfaces.this.notifyBiometricAuthModeChanged();
}
private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
@@ -1630,7 +1592,7 @@ public class StatusBar extends CoreStartable implements
}
}
});
- mStatusBarKeyguardViewManager.registerStatusBar(
+ mStatusBarKeyguardViewManager.registerCentralSurfaces(
/* statusBar= */ this,
mNotificationPanelViewController,
mPanelExpansionStateManager,
@@ -1759,7 +1721,7 @@ public class StatusBar extends CoreStartable implements
getDelegate().onIntentStarted(willAnimate);
if (willAnimate) {
- StatusBar.this.mIsLaunchingActivityOverLockscreen = true;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = true;
}
}
@@ -1769,7 +1731,7 @@ public class StatusBar extends CoreStartable implements
// animation so that we can assume that mIsLaunchingActivityOverLockscreen
// being true means that we will collapse the shade (or at least run the
// post collapse runnables) later on.
- StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
}
@@ -1779,7 +1741,7 @@ public class StatusBar extends CoreStartable implements
// animation so that we can assume that mIsLaunchingActivityOverLockscreen
// being true means that we will collapse the shade (or at least run the
// post collapse runnables) later on.
- StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
getDelegate().onLaunchAnimationCancelled();
}
};
@@ -2603,7 +2565,7 @@ public class StatusBar extends CoreStartable implements
// ordering.
mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
}
- } else if (StatusBar.this.isInLaunchTransition()
+ } else if (CentralSurfaces.this.isInLaunchTransition()
&& mNotificationPanelViewController.isLaunchTransitionFinished()) {
// We are not dismissing the shade, but the launch transition is already
@@ -2626,7 +2588,7 @@ public class StatusBar extends CoreStartable implements
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Trace.beginSection("StatusBar#onReceive");
+ Trace.beginSection("CentralSurfaces#onReceive");
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
@@ -2772,7 +2734,9 @@ public class StatusBar extends CoreStartable implements
void handleVisibleToUserChangedImpl(boolean visibleToUser) {
if (visibleToUser) {
/* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */
+ * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
+ * this.
+ */
boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects =
!mPresenter.isPresenterFullyCollapsed() &&
@@ -2952,7 +2916,7 @@ public class StatusBar extends CoreStartable implements
}
public void showKeyguardImpl() {
- Trace.beginSection("StatusBar#showKeyguard");
+ Trace.beginSection("CentralSurfaces#showKeyguard");
mIsKeyguard = true;
// In case we're locking while a smartspace transition is in progress, reset it.
mKeyguardUnlockAnimationController.resetSmartspaceTransition();
@@ -3075,7 +3039,7 @@ public class StatusBar extends CoreStartable implements
*/
public boolean hideKeyguardImpl(boolean forceStateChange) {
mIsKeyguard = false;
- Trace.beginSection("StatusBar#hideKeyguard");
+ Trace.beginSection("CentralSurfaces#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
int previousState = mStatusBarStateController.getState();
if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
@@ -3192,7 +3156,7 @@ public class StatusBar extends CoreStartable implements
private void updateDozingState() {
Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
- Trace.beginSection("StatusBar#updateDozingState");
+ Trace.beginSection("CentralSurfaces#updateDozingState");
boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
&& !mStatusBarKeyguardViewManager.isOccluded();
@@ -3547,7 +3511,7 @@ public class StatusBar extends CoreStartable implements
@Override
public void onStartedGoingToSleep() {
- String tag = "StatusBar#onStartedGoingToSleep";
+ String tag = "CentralSurfaces#onStartedGoingToSleep";
DejankUtils.startDetectingBlockingIpcs(tag);
updateRevealEffect(false /* wakingUp */);
updateNotificationPanelTouchState();
@@ -3567,7 +3531,7 @@ public class StatusBar extends CoreStartable implements
@Override
public void onStartedWakingUp() {
- String tag = "StatusBar#onStartedWakingUp";
+ String tag = "CentralSurfaces#onStartedWakingUp";
DejankUtils.startDetectingBlockingIpcs(tag);
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mDeviceInteractive = true;
@@ -3642,7 +3606,7 @@ public class StatusBar extends CoreStartable implements
@Override
public void onScreenTurnedOff() {
- Trace.beginSection("StatusBar#onScreenTurnedOff");
+ Trace.beginSection("CentralSurfaces#onScreenTurnedOff");
mFalsingCollector.onScreenOff();
mScrimController.onScreenTurnedOff();
if (mCloseQsBeforeScreenOff) {
@@ -3753,7 +3717,7 @@ public class StatusBar extends CoreStartable implements
@VisibleForTesting
public void updateScrimController() {
- Trace.beginSection("StatusBar#updateScrimController");
+ Trace.beginSection("CentralSurfaces#updateScrimController");
boolean unlocking = mKeyguardStateController.isShowing() && (
mBiometricUnlockController.isWakeAndUnlock()
@@ -4008,8 +3972,14 @@ public class StatusBar extends CoreStartable implements
mActivityLaunchAnimator.startPendingIntentWithAnimation(
controller, animate, intent.getCreatorPackage(),
- (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
- null, getActivityOptions(mDisplayId, animationAdapter)));
+ (animationAdapter) -> {
+ ActivityOptions options = new ActivityOptions(
+ getActivityOptions(mDisplayId, animationAdapter));
+ // TODO b/221255671: restrict this to only be set for notifications
+ options.setEligibleForLegacyPermissionPrompt(true);
+ return intent.sendAndReturnResult(null, 0, null, null, null,
+ null, options.toBundle());
+ });
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -4319,7 +4289,7 @@ public class StatusBar extends CoreStartable implements
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
}
- // TODO: Bring these out of StatusBar.
+ // TODO: Bring these out of CentralSurfaces.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mUserSwitcherController.onDensityOrFontScaleChanged();
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
@@ -4377,7 +4347,7 @@ public class StatusBar extends CoreStartable implements
mDozeServiceHost.updateDozing();
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
- Trace.beginSection("StatusBar#updateKeyguardState");
+ Trace.beginSection("CentralSurfaces#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mNotificationPanelViewController.cancelPendingPanelCollapse();
}
@@ -4400,7 +4370,7 @@ public class StatusBar extends CoreStartable implements
@Override
public void onDozingChanged(boolean isDozing) {
- Trace.beginSection("StatusBar#updateDozing");
+ Trace.beginSection("CentralSurfaces#updateDozing");
mDozing = isDozing;
// Collapse the notification panel if open
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index aeb782660173..536be1c1a866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -59,7 +59,7 @@ import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -70,9 +70,9 @@ import java.util.Optional;
import javax.inject.Inject;
/** */
-@StatusBarComponent.StatusBarScope
-public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
- private final StatusBar mStatusBar;
+@CentralSurfacesComponent.CentralSurfacesScope
+public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
+ private final CentralSurfaces mCentralSurfaces;
private final Context mContext;
private final ShadeController mShadeController;
private final CommandQueue mCommandQueue;
@@ -105,8 +105,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@Inject
- StatusBarCommandQueueCallbacks(
- StatusBar statusBar,
+ CentralSurfacesCommandQueueCallbacks(
+ CentralSurfaces centralSurfaces,
Context context,
@Main Resources resources,
ShadeController shadeController,
@@ -133,7 +133,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mContext = context;
mShadeController = shadeController;
mCommandQueue = commandQueue;
@@ -172,12 +172,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
if (!containsType(types, ITYPE_STATUS_BAR)) {
return;
}
- mStatusBar.clearTransient();
+ mCentralSurfaces.clearTransient();
}
@Override
public void addQsTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null && qsPanelController.getHost() != null) {
qsPanelController.getHost().addTile(tile);
}
@@ -185,7 +185,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void remQsTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null && qsPanelController.getHost() != null) {
qsPanelController.getHost().removeTile(tile);
}
@@ -193,7 +193,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void clickTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null) {
qsPanelController.clickTile(tile);
}
@@ -207,9 +207,9 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void animateExpandNotificationsPanel() {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG,
- "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG,
+ "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -220,9 +220,9 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void animateExpandSettingsPanel(@Nullable String subPanel) {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG,
- "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG,
+ "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -244,7 +244,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void dismissKeyboardShortcutsMenu() {
- mStatusBar.resendMessage(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
+ mCentralSurfaces.resendMessage(CentralSurfaces.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
}
/**
* State is one or more of the DISABLE constants from StatusBarManager.
@@ -257,22 +257,22 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- Log.d(StatusBar.TAG,
+ Log.d(CentralSurfaces.TAG,
mDisableFlagsLogger.getDisableFlagsString(
/* old= */ new DisableFlagsLogger.DisableState(
- mStatusBar.getDisabled1(), mStatusBar.getDisabled2()),
+ mCentralSurfaces.getDisabled1(), mCentralSurfaces.getDisabled2()),
/* new= */ new DisableFlagsLogger.DisableState(
state1, state2BeforeAdjustment),
/* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
state1, state2)));
- final int old1 = mStatusBar.getDisabled1();
+ final int old1 = mCentralSurfaces.getDisabled1();
final int diff1 = state1 ^ old1;
- mStatusBar.setDisabled1(state1);
+ mCentralSurfaces.setDisabled1(state1);
- final int old2 = mStatusBar.getDisabled2();
+ final int old2 = mCentralSurfaces.getDisabled2();
final int diff2 = state2 ^ old2;
- mStatusBar.setDisabled2(state2);
+ mCentralSurfaces.setDisabled2(state2);
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -281,17 +281,17 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- if (mStatusBar.areNotificationAlertsDisabled()) {
+ if (mCentralSurfaces.areNotificationAlertsDisabled()) {
mHeadsUpManager.releaseAllImmediately();
}
}
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
- mStatusBar.updateQsExpansionEnabled();
+ mCentralSurfaces.updateQsExpansionEnabled();
}
if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- mStatusBar.updateQsExpansionEnabled();
+ mCentralSurfaces.updateQsExpansionEnabled();
if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
mShadeController.animateCollapsePanels();
}
@@ -304,8 +304,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
*/
@Override
public void handleSystemKey(int key) {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG, "handleNavigationKey: " + key);
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
}
if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
|| mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
@@ -341,81 +341,82 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void onCameraLaunchGestureDetected(int source) {
- mStatusBar.setLastCameraLaunchSource(source);
- if (mStatusBar.isGoingToSleep()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Finish going to sleep before launching camera");
+ mCentralSurfaces.setLastCameraLaunchSource(source);
+ if (mCentralSurfaces.isGoingToSleep()) {
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Finish going to sleep before launching camera");
}
- mStatusBar.setLaunchCameraOnFinishedGoingToSleep(true);
+ mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
return;
}
if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Can't launch camera right now");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
}
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
"com.android.systemui:CAMERA_GESTURE");
}
vibrateForCameraGesture();
if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
- Log.v(StatusBar.TAG, "Camera launch");
+ Log.v(CentralSurfaces.TAG, "Camera launch");
mKeyguardUpdateMonitor.onCameraLaunched();
}
if (!mStatusBarKeyguardViewManager.isShowing()) {
final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
- mStatusBar.startActivityDismissingKeyguard(cameraIntent,
+ mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
} else {
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
// comes on.
- mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ mCentralSurfaces.acquireGestureWakeLock(
+ CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (isWakingUpOrAwake()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Launching camera");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Launching camera");
}
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
mNotificationPanelViewController.launchCamera(
- mStatusBar.isDeviceInteractive() /* animate */, source);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.isDeviceInteractive() /* animate */, source);
+ mCentralSurfaces.updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Deferring until screen turns on");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Deferring until screen turns on");
}
- mStatusBar.setLaunchCameraOnFinishedWaking(true);
+ mCentralSurfaces.setLaunchCameraOnFinishedWaking(true);
}
}
}
@Override
public void onEmergencyActionLaunchGestureDetected() {
- Intent emergencyIntent = mStatusBar.getEmergencyActionIntent();
+ Intent emergencyIntent = mCentralSurfaces.getEmergencyActionIntent();
if (emergencyIntent == null) {
- Log.wtf(StatusBar.TAG, "Couldn't find an app to process the emergency intent.");
+ Log.wtf(CentralSurfaces.TAG, "Couldn't find an app to process the emergency intent.");
return;
}
if (isGoingToSleep()) {
- mStatusBar.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
+ mCentralSurfaces.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:EMERGENCY_GESTURE");
@@ -424,17 +425,18 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
// app-side haptic experimentation.
if (!mStatusBarKeyguardViewManager.isShowing()) {
- mStatusBar.startActivityDismissingKeyguard(emergencyIntent,
+ mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
// comes on.
- mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ mCentralSurfaces.acquireGestureWakeLock(
+ CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (isWakingUpOrAwake()) {
@@ -448,12 +450,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
- mStatusBar.setLaunchEmergencyActionOnFinishedWaking(true);
+ mCentralSurfaces.setLaunchEmergencyActionOnFinishedWaking(true);
}
@Override
public void onRecentsAnimationStateChanged(boolean running) {
- mStatusBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
+ mCentralSurfaces.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
}
@@ -464,12 +466,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
if (displayId != mDisplayId) {
return;
}
- boolean barModeChanged = mStatusBar.setAppearance(appearance);
+ boolean barModeChanged = mCentralSurfaces.setAppearance(appearance);
mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
- mStatusBar.getBarMode(), navbarColorManagedByIme);
+ mCentralSurfaces.getBarMode(), navbarColorManagedByIme);
- mStatusBar.updateBubblesVisibility();
+ mCentralSurfaces.updateBubblesVisibility();
mStatusBarStateController.setSystemBarAttributes(
appearance, behavior, requestedVisibilities, packageName);
}
@@ -483,12 +485,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
if (!containsType(types, ITYPE_STATUS_BAR)) {
return;
}
- mStatusBar.showTransientUnchecked();
+ mCentralSurfaces.showTransientUnchecked();
}
@Override
public void toggleKeyboardShortcutsMenu(int deviceId) {
- mStatusBar.resendMessage(new StatusBar.KeyboardShortcutsMessage(deviceId));
+ mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
}
@Override
@@ -504,12 +506,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void showPinningEnterExitToast(boolean entering) {
- mStatusBar.showPinningEnterExitToast(entering);
+ mCentralSurfaces.showPinningEnterExitToast(entering);
}
@Override
public void showPinningEscapeToast() {
- mStatusBar.showPinningEscapeToast();
+ mCentralSurfaces.showPinningEscapeToast();
}
@Override
@@ -519,12 +521,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
return;
}
// Show screen pinning request, since this comes from an app, show 'no thanks', button.
- mStatusBar.showScreenPinningRequest(taskId, true);
+ mCentralSurfaces.showScreenPinningRequest(taskId, true);
}
@Override
public void showWirelessChargingAnimation(int batteryLevel) {
- mStatusBar.showWirelessChargingAnimation(batteryLevel);
+ mCentralSurfaces.showWirelessChargingAnimation(batteryLevel);
}
@Override
@@ -534,12 +536,12 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
@Override
public void suppressAmbientDisplay(boolean suppressed) {
- mDozeServiceHost.setDozeSuppressed(suppressed);
+ mDozeServiceHost.setAlwaysOnSuppressed(suppressed);
}
@Override
public void togglePanel() {
- if (mStatusBar.isPanelExpanded()) {
+ if (mCentralSurfaces.isPanelExpanded()) {
mShadeController.animateCollapsePanels();
} else {
animateExpandNotificationsPanel();
@@ -576,8 +578,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
// Make sure to pass -1 for repeat so VibratorManagerService doesn't stop us when going
// to sleep.
return VibrationEffect.createWaveform(
- StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
- StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
+ CentralSurfaces.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
+ CentralSurfaces.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
/* repeat= */ -1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 05fba5498ea7..55b310ff986d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -97,8 +97,8 @@ public final class DozeServiceHost implements DozeHost {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private NotificationPanelViewController mNotificationPanel;
private View mAmbientIndicationContainer;
- private StatusBar mStatusBar;
- private boolean mSuppressed;
+ private CentralSurfaces mCentralSurfaces;
+ private boolean mAlwaysOnSuppressed;
@Inject
public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager,
@@ -146,12 +146,12 @@ public final class DozeServiceHost implements DozeHost {
* Initialize instance with objects only available later during execution.
*/
public void initialize(
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationShadeWindowViewController notificationShadeWindowViewController,
NotificationPanelViewController notificationPanel,
View ambientIndicationContainer) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationPanel = notificationPanel;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
@@ -205,7 +205,7 @@ public final class DozeServiceHost implements DozeHost {
mDozingRequested = true;
updateDozing();
mDozeLog.traceDozing(mStatusBarStateController.isDozing());
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
}
}
@@ -247,13 +247,13 @@ public final class DozeServiceHost implements DozeHost {
&& mWakeLockScreenPerformsAuth;
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
- // are black, indicating that StatusBar is ready to present the rest of the UI.
+ // are black, indicating that CentralSurfaces is ready to present the rest of the UI.
mPulsing = true;
mDozeScrimController.pulse(new PulseCallback() {
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- mStatusBar.updateNotificationPanelTouchState();
+ mCentralSurfaces.updateNotificationPanelTouchState();
setPulsing(true);
}
@@ -261,7 +261,7 @@ public final class DozeServiceHost implements DozeHost {
public void onPulseFinished() {
mPulsing = false;
callback.onPulseFinished();
- mStatusBar.updateNotificationPanelTouchState();
+ mCentralSurfaces.updateNotificationPanelTouchState();
mScrimController.setWakeLockScreenSensorActive(false);
setPulsing(false);
}
@@ -274,14 +274,14 @@ public final class DozeServiceHost implements DozeHost {
if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
}
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
mPulseExpansionHandler.setPulsing(pulsing);
mNotificationWakeUpCoordinator.setPulsing(pulsing);
}
}, reason);
// DozeScrimController is in pulse state, now let's ask ScrimController to start
// pulsing and draw the black frame, if necessary.
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
@Override
@@ -332,15 +332,6 @@ public final class DozeServiceHost implements DozeHost {
}
@Override
- public boolean isBlockingDoze() {
- if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
- Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated");
- return true;
- }
- return false;
- }
-
- @Override
public void extendPulse(int reason) {
if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) {
mScrimController.setWakeLockScreenSensorActive(true);
@@ -412,14 +403,14 @@ public final class DozeServiceHost implements DozeHost {
Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
}
mPendingScreenOffCallback = onDisplayOffCallback;
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
@Override
public void cancelGentleSleep() {
mPendingScreenOffCallback = null;
if (mScrimController.getState() == ScrimState.OFF) {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
}
@@ -452,18 +443,25 @@ public final class DozeServiceHost implements DozeHost {
return mIgnoreTouchWhilePulsing;
}
- void setDozeSuppressed(boolean suppressed) {
- if (suppressed == mSuppressed) {
+ /**
+ * Suppresses always-on-display and waking up the display for notifications.
+ * Does not disable wakeup gestures like pickup and tap.
+ */
+ void setAlwaysOnSuppressed(boolean suppressed) {
+ if (suppressed == mAlwaysOnSuppressed) {
return;
}
- mSuppressed = suppressed;
- mDozeLog.traceDozingSuppressed(mSuppressed);
+ mAlwaysOnSuppressed = suppressed;
for (Callback callback : mCallbacks) {
- callback.onDozeSuppressedChanged(suppressed);
+ callback.onAlwaysOnSuppressedChanged(suppressed);
}
}
- public boolean isDozeSuppressed() {
- return mSuppressed;
+ /**
+ * Whether always-on-display is being suppressed. This does not affect wakeup gestures like
+ * pickup and tap.
+ */
+ public boolean isAlwaysOnSuppressed() {
+ return mAlwaysOnSuppressed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 84103c0bb377..541aeab22a80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -118,7 +118,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
KeyguardStateController.Callback,
AccessibilityController.AccessibilityStateChangedCallback {
- final static String TAG = "StatusBar/KeyguardBottomAreaView";
+ final static String TAG = "CentralSurfaces/KeyguardBottomAreaView";
public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
@@ -169,7 +169,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private FlashlightController mFlashlightController;
private PreviewInflater mPreviewInflater;
private AccessibilityController mAccessibilityController;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private KeyguardAffordanceHelper mAffordanceHelper;
private FalsingManager mFalsingManager;
private boolean mUserSetupComplete;
@@ -274,7 +274,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
};
public void initFrom(KeyguardBottomAreaView oldBottomArea) {
- setStatusBar(oldBottomArea.mStatusBar);
+ setCentralSurfaces(oldBottomArea.mCentralSurfaces);
// if it exists, continue to use the original ambient indication container
// instead of the newly inflated one
@@ -473,8 +473,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mRightAffordanceView.setContentDescription(state.contentDescription);
}
- public void setStatusBar(StatusBar statusBar) {
- mStatusBar = statusBar;
+ public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
+ mCentralSurfaces = centralSurfaces;
updateCameraVisibility(); // in case onFinishInflate() was called too early
}
@@ -732,7 +732,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
- mStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
+ mCentralSurfaces.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
dismissShade, false /* afterKeyguardGone */, true /* deferred */);
}
}
@@ -1019,7 +1019,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public IconState getIcon() {
- boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
+ boolean isCameraDisabled = (mCentralSurfaces != null)
+ && !mCentralSurfaces.isCameraAllowedByAdmin();
mIconState.isVisible = !isCameraDisabled
&& mShowCameraAffordance
&& mUserSetupComplete
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 9bdefcd98422..2c4fc6c6f734 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -14,8 +14,8 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.MULTIUSER_DEBUG;
import android.service.notification.StatusBarNotification;
import android.util.Log;
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 94d6e0b3ad7d..01860a822503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -184,11 +184,12 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.MediaContainerView;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.panelstate.PanelState;
@@ -221,7 +222,7 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Provider;
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationPanelViewController extends PanelViewController
implements NotifPanelEventSource {
@@ -665,6 +666,8 @@ public class NotificationPanelViewController extends PanelViewController
private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
+ private final NotificationListContainer mNotificationListContainer;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -792,7 +795,8 @@ public class NotificationPanelViewController extends PanelViewController
InteractionJankMonitor interactionJankMonitor,
QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ NotificationListContainer notificationListContainer) {
super(view,
falsingManager,
dozeLog,
@@ -823,6 +827,7 @@ public class NotificationPanelViewController extends PanelViewController
mMediaHierarchyManager = mediaHierarchyManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationsQSContainerController = notificationsQSContainerController;
+ mNotificationListContainer = notificationListContainer;
mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
@@ -1127,10 +1132,10 @@ public class NotificationPanelViewController extends PanelViewController
return mKeyguardStatusViewController.hasCustomClock();
}
- private void setStatusBar(StatusBar bar) {
+ private void setCentralSurfaces(CentralSurfaces centralSurfaces) {
// TODO: this can be injected.
- mStatusBar = bar;
- mKeyguardBottomArea.setStatusBar(mStatusBar);
+ mCentralSurfaces = centralSurfaces;
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
}
public void updateResources() {
@@ -1139,8 +1144,9 @@ public class NotificationPanelViewController extends PanelViewController
mSplitShadeNotificationsScrimMarginBottom =
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
- int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
+
+ int panelMarginHorizontal = mResources.getDimensionPixelSize(
+ R.dimen.notification_panel_margin_horizontal);
final boolean newShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mResources);
@@ -1162,11 +1168,12 @@ public class NotificationPanelViewController extends PanelViewController
ensureAllViewsHaveIds(mNotificationContainerParent);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mNotificationContainerParent);
-
+ int statusViewMarginHorizontal = mResources.getDimensionPixelSize(
+ R.dimen.status_view_margin_horizontal);
+ constraintSet.setMargin(R.id.keyguard_status_view, START, statusViewMarginHorizontal);
+ constraintSet.setMargin(R.id.keyguard_status_view, END, statusViewMarginHorizontal);
if (mShouldUseSplitNotificationShade) {
// width = 0 to take up all available space within constraints
- qsWidth = 0;
- panelWidth = 0;
constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END);
constraintSet.connect(
R.id.notification_stack_scroller, START,
@@ -1179,11 +1186,15 @@ public class NotificationPanelViewController extends PanelViewController
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT);
}
}
- constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
- constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
+ constraintSet.setMargin(R.id.notification_stack_scroller, START,
+ mShouldUseSplitNotificationShade ? 0 : panelMarginHorizontal);
+ constraintSet.setMargin(R.id.notification_stack_scroller, END, panelMarginHorizontal);
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
constraintSet.setMargin(R.id.notification_stack_scroller, BOTTOM,
notificationsBottomMargin);
+ constraintSet.setMargin(R.id.qs_frame, START, panelMarginHorizontal);
+ constraintSet.setMargin(R.id.qs_frame, END,
+ mShouldUseSplitNotificationShade ? 0 : panelMarginHorizontal);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
mAmbientState.setStackTopMargin(topMargin);
@@ -1320,7 +1331,7 @@ public class NotificationPanelViewController extends PanelViewController
mAffordanceHelper = new KeyguardAffordanceHelper(
mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
- mKeyguardBottomArea.setStatusBar(mStatusBar);
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
mKeyguardBottomArea.setFalsingManager(mFalsingManager);
mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
@@ -1583,7 +1594,7 @@ public class NotificationPanelViewController extends PanelViewController
float lockIconPadding = 0;
if (mLockIconViewController.getTop() != 0) {
- lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop()
+ lockIconPadding = mCentralSurfaces.getDisplayHeight() - mLockIconViewController.getTop()
+ mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding);
}
@@ -1737,7 +1748,7 @@ public class NotificationPanelViewController extends PanelViewController
mAffordanceHelper.reset(false);
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
if (animate && !isFullyCollapsed()) {
animateCloseQs(true /* animateAway */);
@@ -1836,7 +1847,7 @@ public class NotificationPanelViewController extends PanelViewController
@Override
public void fling(float vel, boolean expand) {
- GestureRecorder gr = mStatusBar.getGestureRecorder();
+ GestureRecorder gr = mCentralSurfaces.getGestureRecorder();
if (gr != null) {
gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
}
@@ -2025,8 +2036,8 @@ public class NotificationPanelViewController extends PanelViewController
mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS
: MetricsEvent.ACTION_SHADE_QS_PULL;
mLockscreenGestureLogger.write(gesture,
- (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
- (int) (vel / mStatusBar.getDisplayDensity()));
+ (int) ((y - mInitialTouchY) / mCentralSurfaces.getDisplayDensity()),
+ (int) (vel / mCentralSurfaces.getDisplayDensity()));
}
private boolean flingExpandsQs(float vel) {
@@ -2304,7 +2315,7 @@ public class NotificationPanelViewController extends PanelViewController
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mQsFalsingThreshold * factor);
}
@@ -2331,7 +2342,7 @@ public class NotificationPanelViewController extends PanelViewController
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0) {
- mStatusBar.requestFaceAuth(false);
+ mCentralSurfaces.requestFaceAuth(false);
}
}
@@ -2342,7 +2353,7 @@ public class NotificationPanelViewController extends PanelViewController
updateQsState();
requestPanelHeightUpdate();
mFalsingCollector.setQsExpanded(expanded);
- mStatusBar.setQsExpanded(expanded);
+ mCentralSurfaces.setQsExpanded(expanded);
mNotificationsQSContainerController.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
@@ -2426,7 +2437,7 @@ public class NotificationPanelViewController extends PanelViewController
if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
&& mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
}
if (DEBUG) {
@@ -3114,7 +3125,7 @@ public class NotificationPanelViewController extends PanelViewController
if (mPanelExpanded != isExpanded) {
mHeadsUpManager.setIsPanelExpanded(isExpanded);
mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
- mStatusBar.setPanelExpanded(isExpanded);
+ mCentralSurfaces.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
if (!isExpanded && mQs != null && mQs.isCustomizing()) {
@@ -3239,7 +3250,7 @@ public class NotificationPanelViewController extends PanelViewController
mKeyguardBottomArea.setImportantForAccessibility(
alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
+ View ambientIndicationContainer = mCentralSurfaces.getAmbientIndicationContainer();
if (ambientIndicationContainer != null) {
ambientIndicationContainer.setAlpha(alpha);
}
@@ -3578,7 +3589,7 @@ public class NotificationPanelViewController extends PanelViewController
@Override
protected void onClosingFinished() {
- mStatusBar.onClosingFinished();
+ mCentralSurfaces.onClosingFinished();
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -3640,7 +3651,7 @@ public class NotificationPanelViewController extends PanelViewController
}
public void clearNotificationEffects() {
- mStatusBar.clearNotificationEffects();
+ mCentralSurfaces.clearNotificationEffects();
}
@Override
@@ -3702,7 +3713,7 @@ public class NotificationPanelViewController extends PanelViewController
* Whether the camera application can be launched for the camera launch gesture.
*/
public boolean canCameraGestureBeLaunched() {
- if (!mStatusBar.isCameraAllowedByAdmin()) {
+ if (!mCentralSurfaces.isCameraAllowedByAdmin()) {
return false;
}
@@ -4046,8 +4057,7 @@ public class NotificationPanelViewController extends PanelViewController
}
public boolean hasPulsingNotifications() {
- return mNotificationStackScrollLayoutController
- .getNotificationListContainer().hasPulsingNotifications();
+ return mNotificationListContainer.hasPulsingNotifications();
}
public ActivatableNotificationView getActivatedChild() {
@@ -4082,10 +4092,10 @@ public class NotificationPanelViewController extends PanelViewController
* @param hideExpandedRunnable a runnable to run when we need to hide the expanded panel.
*/
public void initDependencies(
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
Runnable hideExpandedRunnable,
NotificationShelfController notificationShelfController) {
- setStatusBar(statusBar);
+ setCentralSurfaces(centralSurfaces);
mHideExpandedRunnable = hideExpandedRunnable;
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
@@ -4158,7 +4168,7 @@ public class NotificationPanelViewController extends PanelViewController
initDownStates(event);
// Do not let touches go to shade or QS if the bouncer is visible,
// but still let user swipe down to expand the panel, dismissing the bouncer.
- if (mStatusBar.isBouncerShowing()) {
+ if (mCentralSurfaces.isBouncerShowing()) {
return true;
}
if (mCommandQueue.panelsEnabled()
@@ -4201,8 +4211,8 @@ public class NotificationPanelViewController extends PanelViewController
// Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
// otherwise user would be able to pull down QS or expand the shade.
- if (mStatusBar.isBouncerShowingScrimmed()
- || mStatusBar.isBouncerShowingOverDream()) {
+ if (mCentralSurfaces.isBouncerShowingScrimmed()
+ || mCentralSurfaces.isBouncerShowingOverDream()) {
return false;
}
@@ -4266,15 +4276,15 @@ public class NotificationPanelViewController extends PanelViewController
new PhoneStatusBarView.TouchEventHandler() {
@Override
public void onInterceptTouchEvent(MotionEvent event) {
- mStatusBar.onTouchEvent(event);
+ mCentralSurfaces.onTouchEvent(event);
}
@Override
public boolean handleTouchEvent(MotionEvent event) {
- mStatusBar.onTouchEvent(event);
+ mCentralSurfaces.onTouchEvent(event);
// TODO(b/202981994): Move the touch debugging in this method to a central
- // location. (Right now, it's split between StatusBar and here.)
+ // location. (Right now, it's split between CentralSurfaces and here.)
// If panels aren't enabled, ignore the gesture and don't pass it down to the
// panel view.
@@ -4491,7 +4501,7 @@ public class NotificationPanelViewController extends PanelViewController
: !rightPage;
mIsLaunchTransitionRunning = true;
mLaunchAnimationEndRunnable = null;
- float displayDensity = mStatusBar.getDisplayDensity();
+ float displayDensity = mCentralSurfaces.getDisplayDensity();
int lengthDp = Math.abs((int) (translation / displayDensity));
int velocityDp = Math.abs((int) (vel / displayDensity));
if (start) {
@@ -4499,7 +4509,7 @@ public class NotificationPanelViewController extends PanelViewController
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
mFalsingCollector.onLeftAffordanceOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchLeftAffordance(), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
@@ -4515,7 +4525,7 @@ public class NotificationPanelViewController extends PanelViewController
}
mFalsingCollector.onCameraOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
@@ -4523,7 +4533,7 @@ public class NotificationPanelViewController extends PanelViewController
mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}
- mStatusBar.startLaunchTransitionTimeout();
+ mCentralSurfaces.startLaunchTransitionTimeout();
mBlockTouches = true;
}
@@ -4535,7 +4545,7 @@ public class NotificationPanelViewController extends PanelViewController
mLaunchAnimationEndRunnable.run();
mLaunchAnimationEndRunnable = null;
}
- mStatusBar.readyForKeyguardDone();
+ mCentralSurfaces.readyForKeyguardDone();
}
@Override
@@ -4572,18 +4582,18 @@ public class NotificationPanelViewController extends PanelViewController
mHintAnimationRunning = true;
mAffordanceHelper.startHintAnimation(rightIcon, () -> {
mHintAnimationRunning = false;
- mStatusBar.onHintFinished();
+ mCentralSurfaces.onHintFinished();
});
rightIcon =
mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
: rightIcon;
if (rightIcon) {
- mStatusBar.onCameraHintStarted();
+ mCentralSurfaces.onCameraHintStarted();
} else {
if (mKeyguardBottomArea.isLeftVoiceAssist()) {
- mStatusBar.onVoiceAssistHintStarted();
+ mCentralSurfaces.onVoiceAssistHintStarted();
} else {
- mStatusBar.onPhoneHintStarted();
+ mCentralSurfaces.onPhoneHintStarted();
}
}
}
@@ -4614,7 +4624,7 @@ public class NotificationPanelViewController extends PanelViewController
@Override
public float getAffordanceFalsingFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -5130,7 +5140,7 @@ public class NotificationPanelViewController extends PanelViewController
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
if (state == STATE_OPENING) {
- mStatusBar.makeExpandedVisible(false);
+ mCentralSurfaces.makeExpandedVisible(false);
}
if (state == STATE_CLOSED) {
// Close the status bar in the next frame so we can show the end of the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5caf4f604fcd..0ff010aedfff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -840,7 +840,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
Set<String> mComponentsForcingTopUi = new HashSet<>();
/**
- * The {@link StatusBar} state from the status bar.
+ * The status bar state from {@link CentralSurfaces}.
*/
int mStatusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index fb0e306b2e8b..1e3a02b0606b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -58,7 +58,7 @@ import com.android.systemui.R;
*/
public class NotificationShadeWindowView extends FrameLayout {
public static final String TAG = "NotificationShadeWindowView";
- public static final boolean DEBUG = StatusBar.DEBUG;
+ public static final boolean DEBUG = CentralSurfaces.DEBUG;
private int mRightInset = 0;
private int mLeftInset = 0;
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 396703b11a42..101c86f6dac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
@@ -57,6 +58,7 @@ import javax.inject.Inject;
/**
* Controller for {@link NotificationShadeWindowView}.
*/
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationShadeWindowViewController {
private static final String TAG = "NotifShadeWindowVC";
private final FalsingCollector mFalsingCollector;
@@ -77,8 +79,8 @@ public class NotificationShadeWindowViewController {
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
- private StatusBar mService;
- private NotificationShadeWindowController mNotificationShadeWindowController;
+ private final CentralSurfaces mService;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
@@ -105,7 +107,9 @@ public class NotificationShadeWindowViewController {
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
- Optional<LowLightClockController> lowLightClockController) {
+ Optional<LowLightClockController> lowLightClockController,
+ CentralSurfaces centralSurfaces,
+ NotificationShadeWindowController controller) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mTunerService = tunerService;
@@ -120,6 +124,8 @@ public class NotificationShadeWindowViewController {
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
mLowLightClockController = lowLightClockController;
+ mService = centralSurfaces;
+ mNotificationShadeWindowController = controller;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -452,11 +458,6 @@ public class NotificationShadeWindowViewController {
mStatusBarViewController = statusBarViewController;
}
- public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
- mService = statusBar;
- mNotificationShadeWindowController = controller;
- }
-
/**
* Tell the controller that dozing has begun or ended.
* @param dozing True if dozing has begun.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 249f9886253c..45dc943de2f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,7 +27,7 @@ public abstract class PanelView extends FrameLayout {
public static final String TAG = PanelView.class.getSimpleName();
private PanelViewController.TouchHandler mTouchHandler;
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
protected HeadsUpManagerPhone mHeadsUpManager;
protected KeyguardBottomAreaView mKeyguardBottomArea;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 85e804233ed9..7c1775ed2a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -113,7 +113,7 @@ public abstract class PanelViewController {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
protected HeadsUpManagerPhone mHeadsUpManager;
protected final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@@ -349,9 +349,9 @@ public abstract class PanelViewController {
//TODO: keyguard opens QS a different way; log that too?
// Log the position of the swipe that opened the panel
- float width = mStatusBar.getDisplayWidth();
- float height = mStatusBar.getDisplayHeight();
- int rot = mStatusBar.getRotation();
+ float width = mCentralSurfaces.getDisplayWidth();
+ float height = mCentralSurfaces.getDisplayHeight();
+ int rot = mCentralSurfaces.getRotation();
mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
(int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
@@ -433,10 +433,11 @@ public abstract class PanelViewController {
}
mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
- mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
+ mCentralSurfaces.isFalsingThresholdNeeded(),
+ mCentralSurfaces.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
if (!expand && onKeyguard) {
- float displayDensity = mStatusBar.getDisplayDensity();
+ float displayDensity = mCentralSurfaces.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
@@ -453,7 +454,7 @@ public abstract class PanelViewController {
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
- } else if (!mStatusBar.isBouncerShowing()
+ } else if (!mCentralSurfaces.isBouncerShowing()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
@@ -469,7 +470,7 @@ public abstract class PanelViewController {
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mUnlockFalsingThreshold * factor);
}
@@ -479,14 +480,14 @@ public abstract class PanelViewController {
protected void onTrackingStopped(boolean expand) {
mTracking = false;
- mStatusBar.onTrackingStopped(expand);
+ mCentralSurfaces.onTrackingStopped(expand);
updatePanelExpansionAndVisibility();
}
protected void onTrackingStarted() {
endClosing();
mTracking = true;
- mStatusBar.onTrackingStarted();
+ mCentralSurfaces.onTrackingStarted();
notifyExpandingStarted();
updatePanelExpansionAndVisibility();
}
@@ -556,7 +557,7 @@ public abstract class PanelViewController {
*/
private boolean isFalseTouch(float x, float y,
@Classifier.InteractionType int interactionType) {
- if (!mStatusBar.isFalsingThresholdNeeded()) {
+ if (!mCentralSurfaces.isFalsingThresholdNeeded()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
@@ -921,7 +922,7 @@ public abstract class PanelViewController {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
return;
}
- if (mStatusBar.getNotificationShadeWindowView().isVisibleToUser()) {
+ if (mCentralSurfaces.getNotificationShadeWindowView().isVisibleToUser()) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (mAnimateAfterExpanding) {
notifyExpandingStarted();
@@ -976,11 +977,11 @@ public abstract class PanelViewController {
}
protected void onUnlockHintFinished() {
- mStatusBar.onHintFinished();
+ mCentralSurfaces.onHintFinished();
}
protected void onUnlockHintStarted() {
- mStatusBar.onUnlockHintStarted();
+ mCentralSurfaces.onUnlockHintStarted();
}
public boolean isUnlockHintRunning() {
@@ -1018,7 +1019,7 @@ public abstract class PanelViewController {
View[] viewsToAnimate = {
mKeyguardBottomArea.getIndicationArea(),
- mStatusBar.getAmbientIndicationContainer()};
+ mCentralSurfaces.getAmbientIndicationContainer()};
for (View v : viewsToAnimate) {
if (v == null) {
continue;
@@ -1210,7 +1211,7 @@ public abstract class PanelViewController {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mStatusBar.userActivity();
+ mCentralSurfaces.userActivity();
mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
mMinExpandHeight = 0.0f;
mDownTime = SystemClock.uptimeMillis();
@@ -1340,7 +1341,7 @@ public abstract class PanelViewController {
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
- && !mStatusBar.isBouncerShowing()) {
+ && !mCentralSurfaces.isBouncerShowing()) {
startOpening(event);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index ea61a8b94e7e..c8174669cc65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -37,8 +37,8 @@ class ScreenOffAnimationController @Inject constructor(
private val animations: List<ScreenOffAnimation> =
listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation)
- fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
- animations.forEach { it.initialize(statusBar, lightRevealScrim) }
+ fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
+ animations.forEach { it.initialize(centralSurfaces, lightRevealScrim) }
wakefulnessLifecycle.addObserver(this)
}
@@ -131,7 +131,7 @@ class ScreenOffAnimationController @Inject constructor(
animations.any { it.isKeyguardHideDelayed() }
/**
- * Return true to make the StatusBar expanded so we can animate [LightRevealScrim]
+ * Return true to make the status bar expanded so we can animate [LightRevealScrim]
*/
fun shouldShowLightRevealScrim(): Boolean =
animations.any { it.shouldPlayAnimation() }
@@ -197,7 +197,7 @@ class ScreenOffAnimationController @Inject constructor(
}
interface ScreenOffAnimation {
- fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {}
+ fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {}
/**
* Called when started going to sleep, should return true if the animation will be played
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8d64041ab76b..a3c795f36d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -413,7 +413,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY;
scheduleUpdate();
} else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING) // leaving doze
&& (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
@@ -1255,7 +1255,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
// TODO: remove this. This is necessary because of an order-of-operations limitation.
- // The fix is to move more of these class into @StatusBarScope
+ // The fix is to move more of these class into @CentralSurfacesScope
if (mScrimBehind == null) {
mScrimBehindChangeRunnable = changeRunnable;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index bfd625bbd288..902887027667 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -238,7 +238,7 @@ public enum ScrimState {
mAnimationDuration = mKeyguardFadingAway
? mKeyguardFadingAwayDuration
- : StatusBar.FADE_KEYGUARD_DURATION;
+ : CentralSurfaces.FADE_KEYGUARD_DURATION;
boolean fromAod = previousState == AOD || previousState == PULSING;
mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 24bb7f25f2eb..83ee125f84c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -18,9 +18,9 @@ import com.android.systemui.statusbar.StatusBarState;
/**
* {@link ShadeController} is an abstraction of the work that used to be hard-coded in
- * {@link StatusBar}. The shade itself represents the concept of the status bar window state, and
- * can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of these
- * are coordinated with {@link StatusBarKeyguardViewManager} via
+ * {@link CentralSurfaces}. The shade itself represents the concept of the status bar window state,
+ * and can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of
+ * these are coordinated with {@link StatusBarKeyguardViewManager} via
* {@link com.android.systemui.keyguard.KeyguardViewMediator} and others.
*/
public interface ShadeController {
@@ -38,7 +38,7 @@ public interface ShadeController {
/**
* Collapse the shade animated, showing the bouncer when on {@link StatusBarState#KEYGUARD} or
- * dismissing {@link StatusBar} when on {@link StatusBarState#SHADE}.
+ * dismissing {@link CentralSurfaces} when on {@link StatusBarState#SHADE}.
*/
void animateCollapsePanels(int flags, boolean force);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 53ef97dc317c..cee8b335f380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -47,7 +47,7 @@ public class ShadeControllerImpl implements ShadeController {
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final int mDisplayId;
- protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ protected final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -59,7 +59,7 @@ public class ShadeControllerImpl implements ShadeController {
NotificationShadeWindowController notificationShadeWindowController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
WindowManager windowManager,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<AssistManager> assistManagerLazy
) {
mCommandQueue = commandQueue;
@@ -67,15 +67,15 @@ public class ShadeControllerImpl implements ShadeController {
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
- // TODO: Remove circular reference to StatusBar when possible.
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ // TODO: Remove circular reference to CentralSurfaces when possible.
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAssistManagerLazy = assistManagerLazy;
}
@Override
public void instantExpandNotificationsPanel() {
// Make our window larger and the panel expanded.
- getStatusBar().makeExpandedVisible(true /* force */);
+ getCentralSurfaces().makeExpandedVisible(true /* force */);
getNotificationPanelViewController().expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@@ -110,7 +110,7 @@ public class ShadeControllerImpl implements ShadeController {
}
if (SPEW) {
Log.d(TAG, "animateCollapse():"
- + " mExpandedVisible=" + getStatusBar().isExpandedVisible()
+ + " mExpandedVisible=" + getCentralSurfaces().isExpandedVisible()
+ " flags=" + flags);
}
@@ -124,7 +124,7 @@ public class ShadeControllerImpl implements ShadeController {
// release focus immediately to kick off focus change transition
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
+ getCentralSurfaces().getNotificationShadeWindowViewController().cancelExpandHelper();
getNotificationPanelViewController()
.collapsePanel(true /* animate */, delayed, speedUpFactor);
}
@@ -136,7 +136,7 @@ public class ShadeControllerImpl implements ShadeController {
if (!getNotificationPanelViewController().isFullyCollapsed()) {
mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().visibilityChanged(false);
mAssistManagerLazy.get().hideAssist();
}
return false;
@@ -155,7 +155,7 @@ public class ShadeControllerImpl implements ShadeController {
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- if (getStatusBar().getNotificationShadeWindowView().isVisibleToUser()) {
+ if (getCentralSurfaces().getNotificationShadeWindowView().isVisibleToUser()) {
getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
getNotificationPanelViewController().getView().post(executable);
}
@@ -185,7 +185,7 @@ public class ShadeControllerImpl implements ShadeController {
// close the shade if it was open
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
true /* force */, true /* delayed */);
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().visibilityChanged(false);
return true;
} else {
@@ -201,26 +201,26 @@ public class ShadeControllerImpl implements ShadeController {
runPostCollapseRunnables();
}
} else if (!getPresenter().isPresenterFullyCollapsed()) {
- getStatusBar().instantCollapseNotificationPanel();
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().instantCollapseNotificationPanel();
+ getCentralSurfaces().visibilityChanged(false);
} else {
runPostCollapseRunnables();
}
}
- private StatusBar getStatusBar() {
- return mStatusBarOptionalLazy.get().get();
+ private CentralSurfaces getCentralSurfaces() {
+ return mCentralSurfacesOptionalLazy.get().get();
}
private NotificationPresenter getPresenter() {
- return getStatusBar().getPresenter();
+ return getCentralSurfaces().getPresenter();
}
protected NotificationShadeWindowView getNotificationShadeWindowView() {
- return getStatusBar().getNotificationShadeWindowView();
+ return getCentralSurfaces().getNotificationShadeWindowView();
}
private NotificationPanelViewController getNotificationPanelViewController() {
- return getStatusBar().getPanelController();
+ return getCentralSurfaces().getPanelController();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index a1be5acdac13..7555356af163 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -30,7 +30,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
import java.io.FileDescriptor
@@ -38,7 +38,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Named
-@StatusBarScope
+@CentralSurfacesScope
class SplitShadeHeaderController @Inject constructor(
@Named(SPLIT_SHADE_HEADER) private val statusBar: View,
private val statusBarIconController: StatusBarIconController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 6eeae7f29839..50f21691b044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -22,14 +22,16 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Inject;
-/** Ties the {@link StatusBar} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. */
-@StatusBarComponent.StatusBarScope
+/**
+ * Ties the {@link CentralSurfaces} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}.
+ */
+@CentralSurfacesComponent.CentralSurfacesScope
public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener {
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarWindowController mStatusBarWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 81fb903a87b2..31d9266057da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -48,6 +48,7 @@ import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.util.Assert;
import java.util.ArrayList;
import java.util.List;
@@ -66,6 +67,8 @@ public interface StatusBarIconController {
void addIconGroup(IconManager iconManager);
/** */
void removeIconGroup(IconManager iconManager);
+ /** Refresh the state of an IconManager by recreating the views */
+ void refreshIconGroup(IconManager iconManager);
/** */
void setExternalIcon(String slot);
/** */
@@ -243,6 +246,7 @@ public interface StatusBarIconController {
protected final int mIconSize;
// Whether or not these icons show up in dumpsys
protected boolean mShouldLog = false;
+ private StatusBarIconController mController;
// Enables SystemUI demo mode to take effect in this group
protected boolean mDemoable = true;
@@ -267,13 +271,17 @@ public interface StatusBarIconController {
mDemoable = demoable;
}
+ void setController(StatusBarIconController controller) {
+ mController = controller;
+ }
+
public void setBlockList(@Nullable List<String> blockList) {
+ Assert.isMainThread();
mBlockList.clear();
- if (blockList == null || blockList.isEmpty()) {
- return;
- }
-
mBlockList.addAll(blockList);
+ if (mController != null) {
+ mController.refreshIconGroup(this);
+ }
}
public void setShouldLog(boolean should) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index c5d3937173e7..623ec5e2b762 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -100,6 +100,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
}
}
+ group.setController(this);
mIconGroups.add(group);
List<Slot> allSlots = getSlots();
for (int i = 0; i < allSlots.size(); i++) {
@@ -115,6 +116,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
}
}
+ @Override
+ public void refreshIconGroup(IconManager iconManager) {
+ removeIconGroup(iconManager);
+ addIconGroup(iconManager);
+ }
+
private void refreshIconGroups() {
for (int i = mIconGroups.size() - 1; i >= 0; --i) {
IconManager group = mIconGroups.get(i);
@@ -245,7 +252,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/**
* Accept a list of CallIndicatorIconStates, and show the call strength icons.
- * @param slot StatusBar slot for the call strength icons
+ * @param slot statusbar slot for the call strength icons
* @param states All of the no Calling & SMS icon states
*/
@Override
@@ -272,7 +279,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/**
* Accept a list of CallIndicatorIconStates, and show the no calling icons.
- * @param slot StatusBar slot for the no calling icons
+ * @param slot statusbar slot for the no calling icons
* @param states All of the no Calling & SMS icon states
*/
@Override
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 a96ba56be58d..c160c22203a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -124,8 +124,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onFullyShown() {
updateStates();
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(),
- mStatusBar.getBouncerContainer(), "BOUNCER_VISIBLE");
+ mCentralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(),
+ mCentralSurfaces.getBouncerContainer(), "BOUNCER_VISIBLE");
}
@Override
@@ -175,7 +175,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
@@ -277,16 +277,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
@Override
- public void registerStatusBar(StatusBar statusBar,
+ public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController notificationPanelViewController,
PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mBiometricUnlockController = biometricUnlockController;
- ViewGroup container = mStatusBar.getBouncerContainer();
+ ViewGroup container = mCentralSurfaces.getBouncerContainer();
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
if (panelExpansionStateManager != null) {
@@ -354,13 +354,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
&& mKeyguardUpdateManager.isUdfpsEnrolled()) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
- // StatusBar#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
+ // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
// affordance to enter the device (or swipe up to the input bouncer)
return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing) {
- if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
+ if (!isWakeAndUnlocking() && !mCentralSurfaces.isInLaunchTransition()) {
mBouncer.setExpansion(fraction);
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
@@ -371,7 +371,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mStatusBar.getBouncerContainer(),
+ mCentralSurfaces.wakeUpIfDozing(
+ SystemClock.uptimeMillis(),
+ mCentralSurfaces.getBouncerContainer(),
"BOUNCER_VISIBLE");
}
}
@@ -408,10 +410,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
if (mBouncer.needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
mBouncer.show(true /* resetSecuritySelection */);
} else {
- mStatusBar.showKeyguard();
+ mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
mBouncer.prepare();
@@ -540,7 +542,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationPanelViewController.resetViews(/* animate= */ true);
// Hide bouncer and quick-quick settings.
if (mOccluded && !mDozing) {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
@@ -570,15 +572,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBypassController.setAltBouncerShowing(isShowingAlternateAuth());
if (updateScrim) {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
}
@Override
public void onStartedWakingUp() {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
- NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
view.animate()
@@ -590,9 +592,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onStartedGoingToSleep() {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
- NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
view.animate()
@@ -628,7 +630,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
- * If {@link StatusBar} is pulsing.
+ * If {@link CentralSurfaces} is pulsing.
*/
public void setPulsing(boolean pulsing) {
if (mPulsing != pulsing) {
@@ -649,13 +651,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void setOccluded(boolean occluded, boolean animate) {
- mStatusBar.setOccluded(occluded);
+ mCentralSurfaces.setOccluded(occluded);
if (occluded && !mOccluded && mShowing) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
- if (mStatusBar.isInLaunchTransition()) {
+ if (mCentralSurfaces.isInLaunchTransition()) {
setOccludedAndUpdateStates(true);
- mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
+ mCentralSurfaces.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
new Runnable() {
@Override
public void run() {
@@ -666,7 +668,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return;
}
- if (mStatusBar.isLaunchingActivityOverLockscreen()) {
+ if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
setOccludedAndUpdateStates(true);
// When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
@@ -695,7 +697,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
reset(isOccluding /* hideBouncerWhenShowing*/);
}
if (animate && !occluded && mShowing && !mBouncer.isShowing()) {
- mStatusBar.animateKeyguardUnoccluding();
+ mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@@ -712,7 +714,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void startPreHideAnimation(Runnable finishRunnable) {
if (mBouncer.isShowing()) {
mBouncer.startPreHideAnimation(finishRunnable);
- mStatusBar.onBouncerPreHideAnimation();
+ mCentralSurfaces.onBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
@@ -745,11 +747,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
- if (mStatusBar.isInLaunchTransition()
+ if (mCentralSurfaces.isInLaunchTransition()
|| mKeyguardStateController.isFlingingToDismissKeyguard()) {
final boolean wasFlingingToDismissKeyguard =
mKeyguardStateController.isFlingingToDismissKeyguard();
- mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
+ mCentralSurfaces.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
mNotificationShadeWindowController.setKeyguardShowing(false);
@@ -760,11 +762,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}, new Runnable() {
@Override
public void run() {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
mNotificationShadeWindowController.setKeyguardFadingAway(false);
if (wasFlingingToDismissKeyguard) {
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.finishKeyguardFadingAway();
}
mViewMediatorCallback.keyguardGone();
@@ -783,7 +785,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
delay = 0;
fadeoutDuration = 240;
}
- mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading);
+ mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
@@ -793,11 +795,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationContainer,
fadeoutDuration,
() -> {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
onKeyguardFadedAway();
});
} else {
- mStatusBar.fadeKeyguardWhilePulsing();
+ mCentralSurfaces.fadeKeyguardWhilePulsing();
}
wakeAndUnlockDejank();
} else {
@@ -810,20 +812,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationContainer,
fadeoutDuration,
() -> {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
});
} else {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
}
// hide() will happen asynchronously and might arrive after the scrims
// were already hidden, this means that the transition callback won't
// be triggered anymore and StatusBarWindowController will be forever in
// the fadingAway state.
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
wakeAndUnlockDejank();
} else {
- mStatusBar.hideKeyguard();
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.hideKeyguard();
+ mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
}
}
@@ -866,7 +868,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
@@ -897,7 +899,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void dismissAndCollapse() {
- mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, true, false, true);
}
/**
@@ -922,7 +924,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public boolean onBackPressed(boolean hideImmediately) {
if (mBouncer.isShowing()) {
- mStatusBar.endAffordanceLaunch();
+ mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
hideBouncer(false);
@@ -978,11 +980,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
@Override
public void run() {
- NavigationBarView view = mStatusBar.getNavigationBarView();
+ NavigationBarView view = mCentralSurfaces.getNavigationBarView();
if (view != null) {
view.setVisibility(View.VISIBLE);
}
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.show(navigationBars());
}
};
@@ -1013,7 +1015,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
- mStatusBar.setBouncerShowing(bouncerShowing);
+ mCentralSurfaces.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
@@ -1040,11 +1042,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
}
private View getCurrentNavBarView() {
- final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ final NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
return navBarView != null ? navBarView.getCurrentView() : null;
}
@@ -1052,7 +1054,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* Updates the visibility of the nav bar window (which will cause insets changes).
*/
protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if (mStatusBar.getNavigationBarView() != null) {
+ if (mCentralSurfaces.getNavigationBarView() != null) {
if (navBarVisible) {
long delay = getNavBarShowDelay();
if (delay == 0) {
@@ -1063,7 +1065,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
} else {
mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.hide(navigationBars());
}
}
@@ -1120,7 +1122,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public boolean shouldDisableWindowAnimationsForUnlock() {
- return mStatusBar.isInLaunchTransition();
+ return mCentralSurfaces.isInLaunchTransition();
}
@Override
@@ -1139,7 +1141,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void keyguardGoingAway() {
- mStatusBar.keyguardGoingAway();
+ mCentralSurfaces.keyguardGoingAway();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 09fca100749c..56b6dfc42ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -10,34 +10,34 @@ import com.android.systemui.animation.LaunchAnimator
*/
class StatusBarLaunchAnimatorController(
private val delegate: ActivityLaunchAnimator.Controller,
- private val statusBar: StatusBar,
+ private val centralSurfaces: CentralSurfaces,
private val isLaunchForActivity: Boolean = true
) : ActivityLaunchAnimator.Controller by delegate {
// Always sync the opening window with the shade, given that we draw a hole punch in the shade
// of the same size and position as the opening app to make it visible.
override val openingWindowSyncView: View?
- get() = statusBar.notificationShadeWindowView
+ get() = centralSurfaces.notificationShadeWindowView
override fun onIntentStarted(willAnimate: Boolean) {
delegate.onIntentStarted(willAnimate)
if (!willAnimate) {
- statusBar.collapsePanelOnMainThread()
+ centralSurfaces.collapsePanelOnMainThread()
}
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
- statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
+ centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(
+ centralSurfaces.collapsePanelWithDuration(
ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
}
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
- statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(false)
- statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
+ centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
+ centralSurfaces.onLaunchAnimationEnd(isExpandingFullyAbove)
}
override fun onLaunchAnimationProgress(
@@ -46,11 +46,11 @@ class StatusBarLaunchAnimatorController(
linearProgress: Float
) {
delegate.onLaunchAnimationProgress(state, progress, linearProgress)
- statusBar.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
+ centralSurfaces.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
}
override fun onLaunchAnimationCancelled() {
delegate.onLaunchAnimationCancelled()
- statusBar.onLaunchAnimationCancelled(isLaunchForActivity)
+ centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index ff86d74a86eb..edbddbb3c3e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
-import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -51,9 +51,6 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -75,6 +72,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.wmshell.BubblesManager;
@@ -89,7 +87,8 @@ import dagger.Lazy;
/**
* Status bar implementation of {@link NotificationActivityStarter}.
*/
-public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
+@CentralSurfacesComponent.CentralSurfacesScope
+class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
private final Context mContext;
@@ -123,7 +122,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final NotificationPresenter mPresenter;
private final NotificationPanelViewController mNotificationPanel;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -132,7 +131,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private boolean mIsCollapsingToShowActivityOverLockscreen;
- private StatusBarNotificationActivityStarter(
+ @Inject
+ StatusBarNotificationActivityStarter(
Context context,
CommandQueue commandQueue,
Handler mainThreadHandler,
@@ -162,7 +162,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
MetricsLogger metricsLogger,
StatusBarNotificationActivityStarterLogger logger,
OnUserInteractionCallback onUserInteractionCallback,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
NotificationPresenter presenter,
NotificationPanelViewController panel,
ActivityLaunchAnimator activityLaunchAnimator,
@@ -199,7 +199,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mOnUserInteractionCallback = onUserInteractionCallback;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mPresenter = presenter;
mNotificationPanel = panel;
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -259,7 +259,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
final boolean animate = !willLaunchResolverActivity
- && mStatusBar.shouldAnimateLaunch(isActivityIntent);
+ && mCentralSurfaces.shouldAnimateLaunch(isActivityIntent);
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
@@ -300,7 +300,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mShadeController.addPostCollapseAction(runnable);
mShadeController.collapsePanel(true /* animate */);
} else if (mKeyguardStateController.isShowing()
- && mStatusBar.isOccluded()) {
+ && mCentralSurfaces.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
mShadeController.collapsePanel();
} else {
@@ -475,7 +475,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
try {
ActivityLaunchAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
- mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+ mNotificationAnimationProvider.getAnimatorController(row),
+ mCentralSurfaces,
isActivityIntent);
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
@@ -483,11 +484,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
long eventTime = row.getAndResetLastActionUpTime();
Bundle options = eventTime > 0
? getActivityOptions(
- mStatusBar.getDisplayId(),
+ mCentralSurfaces.getDisplayId(),
adapter,
mKeyguardStateController.isShowing(),
eventTime)
- : getActivityOptions(mStatusBar.getDisplayId(), adapter);
+ : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter);
return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, options);
});
@@ -502,7 +503,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
@Override
public void startNotificationGutsIntent(final Intent intent, final int appUid,
ExpandableNotificationRow row) {
- boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
+ boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */);
ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
@Override
public boolean onDismiss() {
@@ -510,14 +511,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ActivityLaunchAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row),
- mStatusBar, true /* isActivityIntent */);
+ mCentralSurfaces, true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(
animationController, animate, intent.getPackage(),
(adapter) -> TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
- mStatusBar.getDisplayId(),
+ mCentralSurfaces.getDisplayId(),
adapter),
new UserHandle(UserHandle.getUserId(appUid))));
});
@@ -535,7 +536,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
@Override
public void startHistoryIntent(View view, boolean showHistory) {
- boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
+ boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */);
ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
@Override
public boolean onDismiss() {
@@ -555,13 +556,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
);
ActivityLaunchAnimator.Controller animationController =
viewController == null ? null
- : new StatusBarLaunchAnimatorController(viewController, mStatusBar,
+ : new StatusBarLaunchAnimatorController(viewController,
+ mCentralSurfaces,
true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
intent.getPackage(),
(adapter) -> tsb.startActivities(
- getActivityOptions(mStatusBar.getDisplayId(), adapter),
+ getActivityOptions(mCentralSurfaces.getDisplayId(), adapter),
UserHandle.CURRENT));
});
return true;
@@ -615,7 +617,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
entry.getKey());
- mStatusBar.wakeUpForFullScreenIntent();
+ mCentralSurfaces.wakeUpForFullScreenIntent();
fullscreenIntent.send();
entry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
@@ -657,180 +659,4 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
return entry.shouldSuppressFullScreenIntent();
}
-
- // --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
-
- /**
- * Public builder for {@link StatusBarNotificationActivityStarter}.
- */
- @SysUISingleton
- public static class Builder {
- private final Context mContext;
- private final CommandQueue mCommandQueue;
- private final Handler mMainThreadHandler;
-
- private final Executor mUiBgExecutor;
- private final NotificationEntryManager mEntryManager;
- private final NotifPipeline mNotifPipeline;
- private final NotificationVisibilityProvider mVisibilityProvider;
- private final HeadsUpManagerPhone mHeadsUpManager;
- private final ActivityStarter mActivityStarter;
- private final NotificationClickNotifier mClickNotifier;
- private final StatusBarStateController mStatusBarStateController;
- private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final KeyguardManager mKeyguardManager;
- private final IDreamManager mDreamManager;
- private final Optional<BubblesManager> mBubblesManagerOptional;
- private final Lazy<AssistManager> mAssistManagerLazy;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final GroupMembershipManager mGroupMembershipManager;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final ShadeController mShadeController;
- private final KeyguardStateController mKeyguardStateController;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final LockPatternUtils mLockPatternUtils;
- private final StatusBarRemoteInputCallback mRemoteInputCallback;
- private final ActivityIntentHelper mActivityIntentHelper;;
- private final MetricsLogger mMetricsLogger;
- private final StatusBarNotificationActivityStarterLogger mLogger;
- private final OnUserInteractionCallback mOnUserInteractionCallback;
- private final NotifPipelineFlags mNotifPipelineFlags;
- private StatusBar mStatusBar;
- private NotificationPresenter mNotificationPresenter;
- private NotificationPanelViewController mNotificationPanelViewController;
- private ActivityLaunchAnimator mActivityLaunchAnimator;
- private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
-
- @Inject
- public Builder(
- Context context,
- CommandQueue commandQueue,
- @Main Handler mainThreadHandler,
- @UiBackground Executor uiBgExecutor,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
- ActivityStarter activityStarter,
- NotificationClickNotifier clickNotifier,
- StatusBarStateController statusBarStateController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- KeyguardManager keyguardManager,
- IDreamManager dreamManager,
- Optional<BubblesManager> bubblesManager,
- Lazy<AssistManager> assistManagerLazy,
- NotificationRemoteInputManager remoteInputManager,
- GroupMembershipManager groupMembershipManager,
- NotificationLockscreenUserManager lockscreenUserManager,
- ShadeController shadeController,
- KeyguardStateController keyguardStateController,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- LockPatternUtils lockPatternUtils,
- StatusBarRemoteInputCallback remoteInputCallback,
- ActivityIntentHelper activityIntentHelper,
- NotifPipelineFlags notifPipelineFlags,
- MetricsLogger metricsLogger,
- StatusBarNotificationActivityStarterLogger logger,
- OnUserInteractionCallback onUserInteractionCallback) {
-
- mContext = context;
- mCommandQueue = commandQueue;
- mMainThreadHandler = mainThreadHandler;
- mUiBgExecutor = uiBgExecutor;
- mEntryManager = entryManager;
- mNotifPipeline = notifPipeline;
- mVisibilityProvider = visibilityProvider;
- mHeadsUpManager = headsUpManager;
- mActivityStarter = activityStarter;
- mClickNotifier = clickNotifier;
- mStatusBarStateController = statusBarStateController;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
- mKeyguardManager = keyguardManager;
- mDreamManager = dreamManager;
- mBubblesManagerOptional = bubblesManager;
- mAssistManagerLazy = assistManagerLazy;
- mRemoteInputManager = remoteInputManager;
- mGroupMembershipManager = groupMembershipManager;
- mLockscreenUserManager = lockscreenUserManager;
- mShadeController = shadeController;
- mKeyguardStateController = keyguardStateController;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
- mLockPatternUtils = lockPatternUtils;
- mRemoteInputCallback = remoteInputCallback;
- mActivityIntentHelper = activityIntentHelper;
- mNotifPipelineFlags = notifPipelineFlags;
- mMetricsLogger = metricsLogger;
- mLogger = logger;
- mOnUserInteractionCallback = onUserInteractionCallback;
- }
-
- /** Sets the status bar to use as {@link StatusBar}. */
- public Builder setStatusBar(StatusBar statusBar) {
- mStatusBar = statusBar;
- return this;
- }
-
- public Builder setNotificationPresenter(NotificationPresenter notificationPresenter) {
- mNotificationPresenter = notificationPresenter;
- return this;
- }
-
- /** Set the ActivityLaunchAnimator. */
- public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
- mActivityLaunchAnimator = activityLaunchAnimator;
- return this;
- }
-
- /** Set the NotificationLaunchAnimatorControllerProvider. */
- public Builder setNotificationAnimatorControllerProvider(
- NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
- mNotificationAnimationProvider = notificationAnimationProvider;
- return this;
- }
-
- /** Set the NotificationPanelViewController. */
- public Builder setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
- return this;
- }
-
- public StatusBarNotificationActivityStarter build() {
- return new StatusBarNotificationActivityStarter(
- mContext,
- mCommandQueue,
- mMainThreadHandler,
- mUiBgExecutor,
- mEntryManager,
- mNotifPipeline,
- mVisibilityProvider,
- mHeadsUpManager,
- mActivityStarter,
- mClickNotifier,
- mStatusBarStateController,
- mStatusBarKeyguardViewManager,
- mKeyguardManager,
- mDreamManager,
- mBubblesManagerOptional,
- mAssistManagerLazy,
- mRemoteInputManager,
- mGroupMembershipManager,
- mLockscreenUserManager,
- mShadeController,
- mKeyguardStateController,
- mNotificationInterruptStateProvider,
- mLockPatternUtils,
- mRemoteInputCallback,
- mActivityIntentHelper,
- mNotifPipelineFlags,
- mMetricsLogger,
- mLogger,
- mOnUserInteractionCallback,
- mStatusBar,
- mNotificationPresenter,
- mNotificationPanelViewController,
- mActivityLaunchAnimator,
- mNotificationAnimationProvider);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java
new file mode 100644
index 000000000000..caa149e61921
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+public abstract class StatusBarNotificationActivityStarterModule {
+ @Binds
+ abstract NotificationActivityStarter bindActivityStarter(
+ StatusBarNotificationActivityStarter impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c8e1cdc75242..aa061d74f6c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,9 +14,9 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.MULTIUSER_DEBUG;
import android.app.KeyguardManager;
import android.content.Context;
@@ -68,20 +68,25 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.List;
-public class StatusBarNotificationPresenter implements NotificationPresenter,
+import javax.inject.Inject;
+
+@CentralSurfacesComponent.CentralSurfacesScope
+class StatusBarNotificationPresenter implements NotificationPresenter,
ConfigurationController.ConfigurationListener,
NotificationRowBinderImpl.BindRowCallback,
CommandQueue.Callbacks {
private static final String TAG = "StatusBarNotificationPresenter";
- private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ private final ActivityStarter mActivityStarter;
private final KeyguardStateController mKeyguardStateController;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -99,7 +104,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final KeyguardIndicationController mKeyguardIndicationController;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final ShadeController mShadeController;
private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
@@ -110,16 +115,19 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final NotifPipelineFlags mNotifPipelineFlags;
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
+ private final NotificationListContainer mNotifListContainer;
private boolean mReinflateNotificationsOnUserSwitched;
private boolean mDispatchUiModeChangeOnUserSwitched;
private TextView mNotificationPanelDebugText;
protected boolean mVrMode;
- public StatusBarNotificationPresenter(Context context,
+ @Inject
+ StatusBarNotificationPresenter(Context context,
NotificationPanelViewController panel,
HeadsUpManagerPhone headsUp,
NotificationShadeWindowView statusBarWindow,
+ ActivityStarter activityStarter,
NotificationStackScrollLayoutController stackScrollerController,
DozeScrimController dozeScrimController,
ScrimController scrimController,
@@ -127,7 +135,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
@@ -144,14 +152,17 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationRemoteInputManager remoteInputManager,
ConfigurationController configurationController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ NotificationRemoteInputManager.Callback remoteInputManagerCallback,
+ NotificationListContainer notificationListContainer) {
+ mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
mKeyguardIndicationController = keyguardIndicationController;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mShadeController = shadeController;
mShadeTransitionController = shadeTransitionController;
mCommandQueue = commandQueue;
@@ -175,6 +186,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mNotifListContainer = notificationListContainer;
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
@@ -186,14 +198,14 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
}
remoteInputManager.setUpWithCallback(
- Dependency.get(NotificationRemoteInputManager.Callback.class),
+ remoteInputManagerCallback,
mNotificationPanel.createRemoteInputDelegate());
initController.addPostInitTask(() -> {
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotifStackController(),
- stackScrollerController.getNotificationListContainer());
+ mNotifListContainer);
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
@@ -206,9 +218,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
- mGutsManager.setUpWithPresenter(this,
- stackScrollerController.getNotificationListContainer(), mCheckSaveListener,
- mOnSettingsClickListener);
+ mGutsManager.setUpWithPresenter(
+ this, mNotifListContainer, mCheckSaveListener, mOnSettingsClickListener);
// ForegroundServiceNotificationListener adds its listener in its constructor
// but we need to request it here in order for it to be instantiated.
// TODO: figure out how to do this correctly once Dependency.get() is gone.
@@ -341,7 +352,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
updateNotificationViews("user switched");
}
mMediaManager.clearCurrentMediaNotification();
- mStatusBar.setLockscreenUser(newUserId);
+ mCentralSurfaces.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
}
@@ -395,7 +406,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
public void onExpandClicked(NotificationEntry clickedEntry, View clickedView,
boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK");
+ mCentralSurfaces.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK");
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
@@ -463,7 +475,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mStatusBar.isOccluded()) {
+ if (mCentralSurfaces.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -486,7 +498,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
// we don't allow head-up on the lockscreen (unless there's a
// "showWhenLocked" activity currently showing) if
// the potential HUN has a fullscreen intent
- if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) {
+ if (mKeyguardStateController.isShowing() && !mCentralSurfaces.isOccluded()) {
if (DEBUG) {
Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
+ sbn.getKey());
@@ -511,7 +523,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
@Override
public boolean suppressInterruptions(NotificationEntry entry) {
- return mStatusBar.areNotificationAlertsDisabled();
+ return mCentralSurfaces.areNotificationAlertsDisabled();
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
new file mode 100644
index 000000000000..26c483ce18fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+public abstract class StatusBarNotificationPresenterModule {
+ @Binds
+ abstract NotificationPresenter bindPresenter(StatusBarNotificationPresenter impl);
+
+ @Binds
+ abstract NotificationRowBinderImpl.BindRowCallback bindBindRowCallback(
+ StatusBarNotificationPresenter impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index b0206f073011..ee242a4b1b75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -44,7 +44,7 @@ import java.util.Objects;
import javax.inject.Inject;
-/** Controls the signal policies for icons shown in the StatusBar. **/
+/** Controls the signal policies for icons shown in the statusbar. **/
@SysUISingleton
public class StatusBarSignalPolicy implements SignalCallback,
SecurityController.SecurityControllerCallback, Tunable {
@@ -449,7 +449,7 @@ public class StatusBarSignalPolicy implements SignalCallback,
}
/**
- * Stores the StatusBar state for no Calling & SMS.
+ * Stores the statusbar state for no Calling & SMS.
*/
public static class CallIndicatorIconState {
public boolean isNoCalling;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index b742394b18a5..95667cc0b972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -59,7 +59,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
private boolean mForceCollapsedUntilLayout = false;
@@ -119,9 +119,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
}
protected void setup(
- @NonNull StatusBar statusBar,
+ @NonNull CentralSurfaces centralSurfaces,
@NonNull View notificationShadeWindowView) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mNotificationShadeWindowView = notificationShadeWindowView;
mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel);
}
@@ -246,7 +246,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
new OnComputeInternalInsetsListener() {
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
+ if (mIsStatusBarExpanded || mCentralSurfaces.isBouncerShowing()) {
// The touchable region is always the full area when expanded
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
index 26ba31c6f526..582afb1a23ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
@@ -20,7 +20,7 @@ import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_M
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
@@ -32,7 +32,7 @@ import javax.inject.Named;
/**
* Controller for {@link TapAgainView}.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class TapAgainViewController extends ViewController<TapAgainView> {
private final DelayableExecutor mDelayableExecutor;
private final ConfigurationController mConfigurationController;
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 09ff46bed074..c11d450e47b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -60,7 +60,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
private val handler: Handler = Handler()
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
private lateinit var lightRevealScrim: LightRevealScrim
private var animatorDurationScale = 1f
@@ -100,7 +100,8 @@ class UnlockedScreenOffAnimationController @Inject constructor(
}
override fun onAnimationStart(animation: Animator?) {
- interactionJankMonitor.begin(statusBar.notificationShadeWindowView, CUJ_SCREEN_OFF)
+ interactionJankMonitor.begin(
+ mCentralSurfaces.notificationShadeWindowView, CUJ_SCREEN_OFF)
}
})
}
@@ -112,11 +113,11 @@ class UnlockedScreenOffAnimationController @Inject constructor(
}
override fun initialize(
- statusBar: StatusBar,
+ centralSurfaces: CentralSurfaces,
lightRevealScrim: LightRevealScrim
) {
this.lightRevealScrim = lightRevealScrim
- this.statusBar = statusBar
+ this.mCentralSurfaces = centralSurfaces
updateAnimatorDurationScale()
globalSettings.registerContentObserver(
@@ -171,9 +172,9 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// Lock the keyguard if it was waiting for the screen off animation to end.
keyguardViewMediatorLazy.get().maybeHandlePendingLock()
- // Tell the StatusBar to become keyguard for real - we waited on that since
- // it is slow and would have caused the animation to jank.
- statusBar.updateIsKeyguard()
+ // Tell the CentralSurfaces to become keyguard for real - we waited on that
+ // since it is slow and would have caused the animation to jank.
+ mCentralSurfaces.updateIsKeyguard()
// Run the callback given to us by the KeyguardVisibilityHelper.
after.run()
@@ -191,7 +192,8 @@ class UnlockedScreenOffAnimationController @Inject constructor(
override fun onAnimationStart(animation: Animator?) {
interactionJankMonitor.begin(
- statusBar.notificationShadeWindowView, CUJ_SCREEN_OFF_SHOW_AOD)
+ mCentralSurfaces.notificationShadeWindowView,
+ CUJ_SCREEN_OFF_SHOW_AOD)
}
})
.start()
@@ -208,12 +210,12 @@ class UnlockedScreenOffAnimationController @Inject constructor(
override fun onFinishedWakingUp() {
// Set this to false in onFinishedWakingUp rather than onStartedWakingUp so that other
- // observers (such as StatusBar) can ask us whether we were playing the screen off animation
- // and reset accordingly.
+ // observers (such as CentralSurfaces) can ask us whether we were playing the screen off
+ // animation and reset accordingly.
aodUiAnimationPlaying = false
- // If we can't control the screen off animation, we shouldn't mess with the StatusBar's
- // keyguard state unnecessarily.
+ // If we can't control the screen off animation, we shouldn't mess with the
+ // CentralSurfaces's keyguard state unnecessarily.
if (dozeParameters.get().canControlUnlockedScreenOff()) {
// Make sure the status bar is in the correct keyguard state, forcing it if necessary.
// This is required if the screen off animation is cancelled, since it might be
@@ -222,7 +224,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// even if we're going from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have
// changed parts of the UI (such as showing AOD in the shade) without actually changing
// the StatusBarState. This ensures that the UI definitely reflects the desired state.
- statusBar.updateIsKeyguard(true /* forceStateChange */)
+ mCentralSurfaces.updateIsKeyguard(true /* forceStateChange */)
}
}
@@ -244,7 +246,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
// #animateInKeyguard.
- statusBar.notificationPanelViewController.showAodUi()
+ mCentralSurfaces.notificationPanelViewController.showAodUi()
}
}, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
@@ -280,8 +282,8 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
// already expanded and showing notifications/QS, the animation looks really messy. For now,
// disable it if the notification panel is not fully collapsed.
- if ((!this::statusBar.isInitialized ||
- !statusBar.notificationPanelViewController.isFullyCollapsed) &&
+ if ((!this::mCentralSurfaces.isInitialized ||
+ !mCentralSurfaces.notificationPanelViewController.isFullyCollapsed) &&
// Status bar might be expanded because we have started
// playing the animation already
!isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index ad8e79e36661..59c9d0baea3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -22,16 +22,23 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.StatusBarNotifPanelEventSourceModule;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
+import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
-import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
+import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule;
+import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import java.lang.annotation.Documented;
@@ -45,7 +52,7 @@ import dagger.Subcomponent;
/**
* Dagger subcomponent for classes (semi-)related to the status bar. The component is created once
- * inside {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
+ * inside {@link com.android.systemui.statusbar.phone.CentralSurfaces} and never re-created.
*
* TODO(b/197137564): This should likely be re-factored a bit. It includes classes that aren't
* directly related to status bar functionality, like multiple notification classes. And, the fact
@@ -53,81 +60,82 @@ import dagger.Subcomponent;
* outside the component. Should more items be moved *into* this component to avoid so many getters?
*/
@Subcomponent(modules = {
+ NotificationStackScrollLayoutListContainerModule.class,
StatusBarNotifPanelEventSourceModule.class,
- StatusBarViewModule.class
+ StatusBarViewModule.class,
+ StatusBarNotificationActivityStarterModule.class,
+ StatusBarNotificationPresenterModule.class,
})
-@StatusBarComponent.StatusBarScope
-public interface StatusBarComponent {
+@CentralSurfacesComponent.CentralSurfacesScope
+public interface CentralSurfacesComponent {
/**
- * Builder for {@link StatusBarComponent}.
+ * Builder for {@link CentralSurfacesComponent}.
*/
@Subcomponent.Factory
interface Factory {
- StatusBarComponent create();
+ CentralSurfacesComponent create();
}
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
@Scope
- @interface StatusBarScope {}
+ @interface CentralSurfacesScope {}
+
+ /**
+ * Performs initialization logic after {@link CentralSurfacesComponent} has been constructed.
+ */
+ interface Startable {
+ void start();
+ void stop();
+ }
/**
* Creates a {@link NotificationShadeWindowView}.
*/
- @StatusBarScope
NotificationShadeWindowView getNotificationShadeWindowView();
/** */
- @StatusBarScope
NotificationShelfController getNotificationShelfController();
/** */
- @StatusBarScope
NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
/**
* Creates a NotificationShadeWindowViewController.
*/
- @StatusBarScope
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
* Creates a NotificationPanelViewController.
*/
- @StatusBarScope
NotificationPanelViewController getNotificationPanelViewController();
/**
* Creates a LockIconViewController. Must be init after creation.
*/
- @StatusBarScope
LockIconViewController getLockIconViewController();
/**
* Creates an AuthRippleViewController. Must be init after creation.
*/
- @StatusBarScope
AuthRippleController getAuthRippleController();
/**
* Creates a StatusBarHeadsUpChangeListener.
*/
- @StatusBarScope
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
/**
- * Creates a StatusBarCommandQueueCallbacks.
+ * Creates a CentralSurfacesCommandQueueCallbacks.
*/
- @StatusBarScope
- StatusBarCommandQueueCallbacks getStatusBarCommandQueueCallbacks();
+ CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks();
/**
* Creates a SplitShadeHeaderController.
*/
- @StatusBarScope
SplitShadeHeaderController getSplitShadeHeaderController();
/**
@@ -140,20 +148,18 @@ public interface StatusBarComponent {
/**
* Creates a StatusBarInitializer
*/
- @StatusBarScope
StatusBarInitializer getStatusBarInitializer();
/**
- * Set of startables to be run after a StatusBarComponent has been constructed.
+ * Set of startables to be run after a CentralSurfacesComponent has been constructed.
*/
- @StatusBarScope
Set<Startable> getStartables();
- /**
- * Performs initialization logic after {@link StatusBarComponent} has been constructed.
- */
- interface Startable {
- void start();
- void stop();
- }
+ NotificationActivityStarter getNotificationActivityStarter();
+
+ NotificationPresenter getNotificationPresenter();
+
+ NotificationRowBinderImpl.BindRowCallback getBindRowCallback();
+
+ NotificationListContainer getNotificationListContainer();
}
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 e6c239ff9a04..c024c7245c45 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
@@ -92,10 +92,9 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -127,16 +126,16 @@ import dagger.Module;
import dagger.Provides;
/**
- * Dagger Module providing {@link StatusBar}.
+ * Dagger Module providing {@link CentralSurfaces}.
*/
@Module
public interface StatusBarPhoneModule {
/**
- * Provides our instance of StatusBar which is considered optional.
+ * Provides our instance of CentralSurfaces which is considered optional.
*/
@Provides
@SysUISingleton
- static StatusBar provideStatusBar(
+ static CentralSurfaces provideCentralSurfaces(
Context context,
NotificationsController notificationsController,
FragmentService fragmentService,
@@ -196,10 +195,8 @@ public interface StatusBarPhoneModule {
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- StatusBarComponent.Factory statusBarComponentFactory,
+ CentralSurfacesComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
@@ -234,7 +231,7 @@ public interface StatusBarPhoneModule {
DeviceStateManager deviceStateManager,
DreamOverlayStateController dreamOverlayStateController,
WiredChargingRippleController wiredChargingRippleController) {
- return new StatusBar(
+ return new CentralSurfaces(
context,
notificationsController,
fragmentService,
@@ -296,7 +293,6 @@ public interface StatusBarPhoneModule {
commandQueue,
statusBarComponentFactory,
pluginManager,
- statusBarNotificationActivityStarterBuilder,
shadeController,
statusBarKeyguardViewManager,
viewMediatorCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index ebd58fb8ebed..79fe700191f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -61,6 +61,9 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -77,7 +80,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShadeWindowView providesNotificationShadeWindowView(
LayoutInflater layoutInflater) {
NotificationShadeWindowView notificationShadeWindowView = (NotificationShadeWindowView)
@@ -92,7 +95,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller);
@@ -100,7 +103,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
NotificationStackScrollLayout notificationStackScrollLayout) {
NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
@@ -115,7 +118,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShelfController providesStatusBarWindowView(
NotificationShelfComponent.Builder notificationShelfComponentBuilder,
NotificationShelf notificationShelf) {
@@ -131,7 +134,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationPanelView getNotificationPanelView(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.getNotificationPanelView();
@@ -139,7 +142,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static LockIconView getLockIconView(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.lock_icon_view);
@@ -147,7 +150,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Nullable
public static AuthRippleView getAuthRippleView(
NotificationShadeWindowView notificationShadeWindowView) {
@@ -157,7 +160,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
@Named(SPLIT_SHADE_HEADER)
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static View getSplitShadeStatusBarView(
NotificationShadeWindowView notificationShadeWindowView,
FeatureFlags featureFlags) {
@@ -172,7 +175,7 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.privacy_chip);
@@ -180,21 +183,21 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.statusIcons);
}
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_VIEW)
static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
return view.findViewById(R.id.batteryRemainingIcon);
}
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_CONTROLLER)
static BatteryMeterViewController getBatteryMeterViewController(
@Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView,
@@ -218,14 +221,14 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
}
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
@@ -235,9 +238,9 @@ public abstract class StatusBarViewModule {
* Creates a new {@link CollapsedStatusBarFragment}.
*
* **IMPORTANT**: This method intentionally does not have
- * {@link StatusBarComponent.StatusBarScope}, which means a new fragment *will* be created each
- * time this method is called. This is intentional because we need fragments to re-created in
- * certain lifecycle scenarios.
+ * {@link CentralSurfacesComponent.CentralSurfacesScope}, which means a new fragment *will* be
+ * created each time this method is called. This is intentional because we need fragments to
+ * re-created in certain lifecycle scenarios.
*
* This provider is {@link Named} such that it does not conflict with the provider inside of
* {@link StatusBarFragmentComponent}.
@@ -260,7 +263,9 @@ public abstract class StatusBarViewModule {
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
- OperatorNameViewController.Factory operatorNameViewControllerFactory
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
+ SecureSettings secureSettings,
+ @Main Executor mainExecutor
) {
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
ongoingCallController,
@@ -277,6 +282,8 @@ public abstract class StatusBarViewModule {
statusBarStateController,
commandQueue,
collapsedStatusBarFragmentLogger,
- operatorNameViewControllerFactory);
+ operatorNameViewControllerFactory,
+ secureSettings,
+ mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2af077255869..2c84219dbfd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -30,8 +30,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Fragment;
+import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Parcelable;
+import android.provider.Settings;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -39,8 +41,11 @@ import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.LinearLayout;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -66,9 +71,11 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
@@ -110,6 +117,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final PanelExpansionStateManager mPanelExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final SecureSettings mSecureSettings;
+ private final Executor mMainExecutor;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -145,7 +154,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
- OperatorNameViewController.Factory operatorNameViewControllerFactory
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
+ SecureSettings secureSettings,
+ @Main Executor mainExecutor
) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
@@ -163,6 +174,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCommandQueue = commandQueue;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
+ mSecureSettings = secureSettings;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -187,10 +200,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
mDarkIconManager.setShouldLog(true);
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
- mDarkIconManager.setBlockList(mBlockedIcons);
+ updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
@@ -203,6 +213,24 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mAnimationScheduler.addCallback(this);
}
+ @VisibleForTesting
+ void updateBlockedIcons() {
+ mBlockedIcons.clear();
+
+ if (mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0) {
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
+ }
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
+
+ mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons));
+ }
+
+ @VisibleForTesting
+ List<String> getBlockedIcons() {
+ return mBlockedIcons;
+ }
+
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -217,6 +245,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
initOngoingCallChip();
+
+ mSecureSettings.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
+ false,
+ mVolumeSettingObserver);
}
@Override
@@ -225,6 +258,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
mOngoingCallController.removeCallback(mOngoingCallListener);
+ mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
}
@Override
@@ -584,6 +618,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin);
}
+ private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBlockedIcons();
+ }
+ };
+
// Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
private View.OnLayoutChangeListener mStatusBarLayoutListener =
(view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 22b7f649775e..2eba325ff63d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,12 +18,14 @@ package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
@@ -39,12 +41,6 @@ import dagger.Subcomponent;
*
* Anything that depends on {@link CollapsedStatusBarFragment} or {@link PhoneStatusBarView}
* should be included here or in {@link StatusBarFragmentModule}.
- *
- * Note that this is completely separate from
- * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent}. This component gets
- * re-created on each new fragment creation, whereas
- * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent} is only created once in
- * {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
*/
@Subcomponent(modules = {StatusBarFragmentModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index de05eb1e42ca..982ccfd4dffa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -69,13 +69,10 @@ class OngoingCallController @Inject constructor(
private var isFullscreen: Boolean = false
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
- /** True if the application managing the call is visible to the user. */
- private var isCallAppVisible: Boolean = false
private var chipView: View? = null
- private var uidObserver: IUidObserver.Stub? = null
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
-
+ private val uidObserver = CallAppUidObserver()
private val notifListener = object : NotifCollectionListener {
// Temporary workaround for b/178406514 for testing purposes.
//
@@ -160,7 +157,7 @@ class OngoingCallController @Inject constructor(
fun hasOngoingCall(): Boolean {
return callNotificationInfo?.isOngoing == true &&
// When the user is in the phone app, don't show the chip.
- !isCallAppVisible
+ !uidObserver.isCallAppVisible
}
override fun addCallback(listener: OngoingCallListener) {
@@ -196,7 +193,7 @@ class OngoingCallController @Inject constructor(
}
updateChipClickListener()
- setUpUidObserver(currentCallNotificationInfo)
+ uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
statusBarWindowController.ifPresent {
it.setOngoingProcessRequiresStatusBarVisible(true)
@@ -240,64 +237,6 @@ class OngoingCallController @Inject constructor(
}
}
- /**
- * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call.
- */
- private fun setUpUidObserver(currentCallNotificationInfo: CallNotificationInfo) {
- try {
- isCallAppVisible = isProcessVisibleToUser(
- iActivityManager.getUidProcessState(
- currentCallNotificationInfo.uid, context.opPackageName
- )
- )
- } catch (se: SecurityException) {
- Log.e(TAG, "Security exception when trying to get process state: $se")
- return
- }
-
- if (uidObserver != null) {
- iActivityManager.unregisterUidObserver(uidObserver)
- }
-
- uidObserver = object : IUidObserver.Stub() {
- override fun onUidStateChanged(
- uid: Int,
- procState: Int,
- procStateSeq: Long,
- capability: Int
- ) {
- if (uid == currentCallNotificationInfo.uid) {
- val oldIsCallAppVisible = isCallAppVisible
- isCallAppVisible = isProcessVisibleToUser(procState)
- if (oldIsCallAppVisible != isCallAppVisible) {
- // Animations may be run as a result of the call's state change, so ensure
- // the listener is notified on the main thread.
- mainExecutor.execute {
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- }
- }
- }
- }
-
- override fun onUidGone(uid: Int, disabled: Boolean) {}
- override fun onUidActive(uid: Int) {}
- override fun onUidIdle(uid: Int, disabled: Boolean) {}
- override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
- }
-
- try {
- iActivityManager.registerUidObserver(
- uidObserver,
- ActivityManager.UID_OBSERVER_PROCSTATE,
- ActivityManager.PROCESS_STATE_UNKNOWN,
- context.opPackageName
- )
- } catch (se: SecurityException) {
- Log.e(TAG, "Security exception when trying to register uid observer: $se")
- return
- }
- }
-
/** Returns true if the given [procState] represents a process that's visible to the user. */
private fun isProcessVisibleToUser(procState: Int): Boolean {
return procState <= ActivityManager.PROCESS_STATE_TOP
@@ -321,9 +260,7 @@ class OngoingCallController @Inject constructor(
statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- if (uidObserver != null) {
- iActivityManager.unregisterUidObserver(uidObserver)
- }
+ uidObserver.unregister()
}
/** Tear down anything related to the chip view to prevent leaks. */
@@ -380,7 +317,84 @@ class OngoingCallController @Inject constructor(
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("Active call notification: $callNotificationInfo")
- pw.println("Call app visible: $isCallAppVisible")
+ pw.println("Call app visible: ${uidObserver.isCallAppVisible}")
+ }
+
+ /** Our implementation of a [IUidObserver]. */
+ inner class CallAppUidObserver : IUidObserver.Stub() {
+ /** True if the application managing the call is visible to the user. */
+ var isCallAppVisible: Boolean = false
+ private set
+
+ /** The UID of the application managing the call. Null if there is no active call. */
+ private var callAppUid: Int? = null
+
+ /**
+ * True if this observer is currently registered with the activity manager and false
+ * otherwise.
+ */
+ private var isRegistered = false
+
+
+ /** Register this observer with the activity manager and the given [uid]. */
+ fun registerWithUid(uid: Int) {
+ if (callAppUid == uid) {
+ return
+ }
+ callAppUid = uid
+
+ try {
+ isCallAppVisible = isProcessVisibleToUser(
+ iActivityManager.getUidProcessState(uid, context.opPackageName)
+ )
+ if (isRegistered) {
+ return
+ }
+ iActivityManager.registerUidObserver(
+ uidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_UNKNOWN,
+ context.opPackageName
+ )
+ isRegistered = true
+ } catch (se: SecurityException) {
+ Log.e(TAG, "Security exception when trying to set up uid observer: $se")
+ }
+ }
+
+ /** Unregister this observer with the activity manager. */
+ fun unregister() {
+ callAppUid = null
+ isRegistered = false
+ iActivityManager.unregisterUidObserver(uidObserver)
+ }
+
+ override fun onUidStateChanged(
+ uid: Int,
+ procState: Int,
+ procStateSeq: Long,
+ capability: Int
+ ) {
+ val currentCallAppUid = callAppUid ?: return
+ if (uid != currentCallAppUid) {
+ return
+ }
+
+ val oldIsCallAppVisible = isCallAppVisible
+ isCallAppVisible = isProcessVisibleToUser(procState)
+ if (oldIsCallAppVisible != isCallAppVisible) {
+ // Animations may be run as a result of the call's state change, so ensure
+ // the listener is notified on the main thread.
+ mainExecutor.execute {
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ }
+ }
+ }
+
+ override fun onUidGone(uid: Int, disabled: Boolean) {}
+ override fun onUidActive(uid: Int) {}
+ override fun onUidIdle(uid: Int, disabled: Boolean) {}
+ override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
index 3a1491400f38..60f6df66cb5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
@@ -25,7 +25,7 @@ import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
/**
@@ -80,8 +80,8 @@ class StatusBarWindowStateController @Inject constructor(
}
windowState = state
- if (StatusBar.DEBUG_WINDOW_STATE) {
- Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state))
+ if (CentralSurfaces.DEBUG_WINDOW_STATE) {
+ Log.d(CentralSurfaces.TAG, "Status bar " + windowStateToString(state))
}
listeners.forEach { it.onStatusBarWindowStateChanged(state) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index e2374ada15d5..d6dfceac7a63 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -26,7 +26,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
@@ -50,7 +50,7 @@ constructor(
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
private var isFolded = false
private var isFoldHandled = true
@@ -66,14 +66,14 @@ constructor(
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
private val startAnimationRunnable = Runnable {
- statusBar.notificationPanelViewController.startFoldToAodAnimation {
+ mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation {
// End action
setAnimationState(playing = false)
}
}
- override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
- this.statusBar = statusBar
+ override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
+ this.mCentralSurfaces = centralSurfaces
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
@@ -88,7 +88,7 @@ constructor(
globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
) {
setAnimationState(playing = true)
- statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
true
} else {
setAnimationState(playing = false)
@@ -98,7 +98,7 @@ constructor(
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
handler.removeCallbacks(startAnimationRunnable)
- statusBar.notificationPanelViewController.cancelFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -131,12 +131,14 @@ constructor(
// We should play the folding to AOD animation
setAnimationState(playing = true)
- statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
- OneShotPreDrawListener.add(statusBar.notificationPanelViewController.view, onReady)
+ OneShotPreDrawListener.add(
+ mCentralSurfaces.notificationPanelViewController.view, onReady
+ )
} else {
// No animation, call ready callback immediately
onReady.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 796af115bf68..58b4af43a9b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -134,6 +134,16 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
}
@Test
+ public void moveWindowMagnifierToPosition() throws RemoteException {
+ mIWindowMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY,
+ 100f, 200f, mAnimationCallback);
+ waitForIdleSync();
+
+ verify(mWindowMagnificationController).moveWindowMagnifierToPosition(
+ eq(100f), eq(200f), any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
public void showMagnificationButton() throws RemoteException {
mIWindowMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
new file mode 100644
index 000000000000..30bff0943da7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.os.RemoteException;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MockMagnificationAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub {
+
+ private final CountDownLatch mCountDownLatch;
+ private final AtomicInteger mSuccessCount;
+ private final AtomicInteger mFailedCount;
+
+ MockMagnificationAnimationCallback(CountDownLatch countDownLatch) {
+ mCountDownLatch = countDownLatch;
+ mSuccessCount = new AtomicInteger();
+ mFailedCount = new AtomicInteger();
+ }
+
+ public int getSuccessCount() {
+ return mSuccessCount.get();
+ }
+
+ public int getFailedCount() {
+ return mFailedCount.get();
+ }
+
+ @Override
+ public void onResult(boolean success) throws RemoteException {
+ mCountDownLatch.countDown();
+ if (success) {
+ mSuccessCount.getAndIncrement();
+ } else {
+ mFailedCount.getAndIncrement();
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 3cc177dd8d91..21c3d6ea0660 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -60,6 +60,8 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@Ignore
@@ -218,6 +220,29 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
+ public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()
+ throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(2);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ animationCallback);
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+ Float.NaN, Float.NaN, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ // The callback in 2nd enableWindowMagnification will return true
+ assertEquals(1, animationCallback.getSuccessCount());
+ // The callback in 1st enableWindowMagnification will return false
+ assertEquals(1, animationCallback.getFailedCount());
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ @Test
public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
@@ -425,6 +450,102 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
+ public void moveWindowMagnifierToPosition_enabled_expectedValues()
+ throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+ enableWindowMagnificationWithoutAnimation();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ targetCenterX, targetCenterY, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ assertEquals(1, animationCallback.getSuccessCount());
+ assertEquals(0, animationCallback.getFailedCount());
+ verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
+ }
+
+ @Test
+ public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()
+ throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(4);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+ enableWindowMagnificationWithoutAnimation();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ // only the last one callback will return true
+ assertEquals(1, animationCallback.getSuccessCount());
+ // the others will return false
+ assertEquals(3, animationCallback.getFailedCount());
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40);
+ }
+
+ @Test
+ public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()
+ throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(2);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ animationCallback);
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ targetCenterX, targetCenterY, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ // The callback in moveWindowMagnifierToPosition will return true
+ assertEquals(1, animationCallback.getSuccessCount());
+ // The callback in enableWindowMagnification will return false
+ assertEquals(1, animationCallback.getFailedCount());
+ verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
+ }
+
+ @Test
+ public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()
+ throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(2);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ animationCallback);
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ Float.NaN, Float.NaN, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ // The callback in moveWindowMagnifierToPosition will return true
+ assertEquals(1, animationCallback.getSuccessCount());
+ // The callback in enableWindowMagnification will return false
+ assertEquals(1, animationCallback.getFailedCount());
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ @Test
public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
@@ -569,6 +690,20 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f);
}
+ @Test
+ public void moveWindowMagnifierToPosition_enabled() {
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+ enableWindowMagnificationWithoutAnimation();
+
+ mInstrumentation.runOnMainSync(
+ () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
+ mAnimationCallback));
+ SystemClock.sleep(mWaitingAnimationPeriod);
+
+ verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
+ }
+
private void verifyFinalSpec(float expectedScale, float expectedCenterX,
float expectedCenterY) {
assertEquals(expectedScale, mController.getScale(), 0f);
@@ -663,6 +798,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Override
+ void moveWindowMagnifierToPosition(float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ super.moveWindowMagnifierToPosition(positionX, positionY, callback);
+ mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback);
+ }
+
+ @Override
void setScale(float scale) {
super.setScale(scale);
mSpyController.setScale(scale);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 6e5926db519d..a49c4d76445b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -40,6 +40,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -88,6 +89,8 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
@@ -96,12 +99,16 @@ import java.util.concurrent.atomic.AtomicInteger;
public class WindowMagnificationControllerTest extends SysuiTestCase {
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
+ private static final long ANIMATION_DURATION_MS = 300;
+ private final long mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
@Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MirrorWindowControl mMirrorWindowControl;
@Mock
private WindowMagnifierCallback mWindowMagnifierCallback;
+ @Mock
+ IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -287,6 +294,82 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
+ public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
+ throws InterruptedException {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, 0, 0, null);
+ });
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+ final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+ .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
+ final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifierToPosition(
+ targetCenterX, targetCenterY, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ assertEquals(1, animationCallback.getSuccessCount());
+ assertEquals(0, animationCallback.getFailedCount());
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+ .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ assertEquals(mWindowMagnificationController.getCenterX(),
+ sourceBoundsCaptor.getValue().exactCenterX(), 0);
+ assertEquals(mWindowMagnificationController.getCenterY(),
+ sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
+ assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
+ }
+
+ @Test
+ public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
+ throws InterruptedException {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, 0, 0, null);
+ });
+ final CountDownLatch countDownLatch = new CountDownLatch(4);
+ final MockMagnificationAnimationCallback animationCallback =
+ new MockMagnificationAnimationCallback(countDownLatch);
+ final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+ .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
+ final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifierToPosition(
+ centerX + 10, centerY + 10, animationCallback);
+ mWindowMagnificationController.moveWindowMagnifierToPosition(
+ centerX + 20, centerY + 20, animationCallback);
+ mWindowMagnificationController.moveWindowMagnifierToPosition(
+ centerX + 30, centerY + 30, animationCallback);
+ mWindowMagnificationController.moveWindowMagnifierToPosition(
+ centerX + 40, centerY + 40, animationCallback);
+ });
+
+ assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
+ // only the last one callback will return true
+ assertEquals(1, animationCallback.getSuccessCount());
+ // the others will return false
+ assertEquals(3, animationCallback.getFailedCount());
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+ .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ assertEquals(mWindowMagnificationController.getCenterX(),
+ sourceBoundsCaptor.getValue().exactCenterX(), 0);
+ assertEquals(mWindowMagnificationController.getCenterY(),
+ sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
+ assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
+ }
+
+ @Test
public void setScale_enabled_expectedValueAndUpdateStateDescription() {
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
@@ -484,6 +567,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
assertTrue(
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+ verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index d3f30c508b8b..ccf2f8b16f8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -148,13 +148,13 @@ public class WindowMagnificationTest extends SysuiTestCase {
}
@Test
- public void onDrag_enabled_notifyCallback() throws RemoteException {
+ public void onMove_enabled_notifyCallback() throws RemoteException {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
- mWindowMagnification.onDrag(TEST_DISPLAY);
+ mWindowMagnification.onMove(TEST_DISPLAY);
- verify(mConnectionCallback).onDrag(TEST_DISPLAY);
+ verify(mConnectionCallback).onMove(TEST_DISPLAY);
}
@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
deleted file mode 100644
index 619d48d1e306..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ /dev/null
@@ -1,344 +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 android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-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;
-import android.os.Bundle;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-@SmallTest
-public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
-
- @Mock AuthBiometricView.Callback mCallback;
-
- private AuthBiometricFaceToFingerprintView mFaceToFpView;
-
- @Mock private Button mNegativeButton;
- @Mock private Button mCancelButton;
- @Mock private Button mConfirmButton;
- @Mock private Button mUseCredentialButton;
- @Mock private Button mTryAgainButton;
-
- @Mock private TextView mTitleView;
- @Mock private TextView mSubtitleView;
- @Mock private TextView mDescriptionView;
- @Mock private TextView mIndicatorView;
- @Mock private ImageView mIconView;
- @Mock private View mIconHolderView;
- @Mock private AuthBiometricFaceView.IconController mFaceIconController;
- @Mock private AuthBiometricFaceToFingerprintView.UdfpsIconController mUdfpsIconController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mFaceToFpView = new TestableView(mContext);
- mFaceToFpView.mFaceIconController = mFaceIconController;
- mFaceToFpView.mUdfpsIconController = mUdfpsIconController;
- mFaceToFpView.setCallback(mCallback);
-
- mFaceToFpView.mNegativeButton = mNegativeButton;
- mFaceToFpView.mCancelButton = mCancelButton;
- mFaceToFpView.mUseCredentialButton = mUseCredentialButton;
- mFaceToFpView.mConfirmButton = mConfirmButton;
- mFaceToFpView.mTryAgainButton = mTryAgainButton;
- mFaceToFpView.mIndicatorView = mIndicatorView;
- }
-
- @Test
- public void testStateUpdated_whenDialogAnimatedIn() {
- mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
- verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
- }
-
- @Test
- public void testIconUpdatesState_whenDialogStateUpdated() {
- mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
- verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
-
- mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
- verify(mFaceToFpView.mFaceIconController).updateState(
- eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
- eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
- verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
-
- assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
- }
-
- @Test
- public void testStateUpdated_whenSwitchToFingerprint() {
- mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
-
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
-
- verify(mFaceToFpView.mFaceIconController).deactivate();
- verify(mFaceToFpView.mUdfpsIconController).updateState(
- eq(AuthBiometricFaceToFingerprintView.STATE_IDLE));
- verify(mConfirmButton).setVisibility(eq(View.GONE));
-
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
-
- verify(mFaceToFpView.mUdfpsIconController).updateState(
- eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
- }
-
- @Test
- public void testStateUpdated_whenSwitchToFingerprint_invokesCallbacks() {
- class TestModalityListener implements ModalityListener {
- public int switchCount = 0;
-
- @Override
- public void onModalitySwitched(int oldModality, int newModality) {
- assertEquals(TYPE_FINGERPRINT, newModality);
- assertEquals(TYPE_FACE, oldModality);
- switchCount++;
- }
- }
- final TestModalityListener modalityListener = new TestModalityListener();
-
- mFaceToFpView.onDialogAnimatedIn();
- mFaceToFpView.setModalityListener(modalityListener);
-
- assertEquals(0, modalityListener.switchCount);
-
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
-
- assertEquals(1, modalityListener.switchCount);
- }
-
- @Test
- @Ignore("flaky, b/189031816")
- public void testModeUpdated_onSoftError_whenSwitchToFingerprint() {
- mFaceToFpView.onDialogAnimatedIn();
- mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
- waitForIdleSync();
-
- verify(mIndicatorView).setText(
- eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
- verify(mCallback).onAction(
- eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-
- // First we enter the error state, since we need to show the error animation/text. The
- // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
- assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
- }
-
- @Test
- @Ignore("flaky, b/189031816")
- public void testModeUpdated_onHardError_whenSwitchToFingerprint() {
- mFaceToFpView.onDialogAnimatedIn();
- mFaceToFpView.onError(TYPE_FACE, "oh no!");
- waitForIdleSync();
-
- verify(mIndicatorView).setText(
- eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
- verify(mCallback).onAction(
- eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-
- // First we enter the error state, since we need to show the error animation/text. The
- // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
- assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
- }
-
- @Test
- public void testFingerprintOnlyStartsOnFirstError() {
- mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
-
- mFaceToFpView.onDialogAnimatedIn();
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
-
- reset(mCallback);
-
- mFaceToFpView.onError(TYPE_FACE, "oh no!");
- mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
-
- verify(mCallback, never()).onAction(
- eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
- }
-
- @Test
- public void testOnSaveState() {
- final FingerprintSensorPropertiesInternal sensorProps = createFingerprintSensorProps();
- mFaceToFpView.setFingerprintSensorProps(sensorProps);
-
- final Bundle savedState = new Bundle();
- mFaceToFpView.onSaveState(savedState);
-
- assertEquals(savedState.getInt(AuthDialog.KEY_BIOMETRIC_SENSOR_TYPE),
- mFaceToFpView.getActiveSensorType());
- assertEquals(savedState.getParcelable(AuthDialog.KEY_BIOMETRIC_SENSOR_PROPS), sensorProps);
- }
-
- @Test
- public void testRestoreState() {
- final Bundle savedState = new Bundle();
- savedState.putInt(AuthDialog.KEY_BIOMETRIC_SENSOR_TYPE, TYPE_FINGERPRINT);
- savedState.putParcelable(AuthDialog.KEY_BIOMETRIC_SENSOR_PROPS,
- createFingerprintSensorProps());
-
- mFaceToFpView.restoreState(savedState);
-
- assertEquals(mFaceToFpView.getActiveSensorType(), TYPE_FINGERPRINT);
- assertTrue(mFaceToFpView.isFingerprintUdfps());
- }
-
- @NonNull
- private static FingerprintSensorPropertiesInternal createFingerprintSensorProps() {
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("componentId", "hardwareVersion",
- "firmwareVersion", "serialNumber", "softwareVersion"));
-
- return new FingerprintSensorPropertiesInternal(
- 0 /* sensorId */,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */,
- List.of(new SensorLocationInternal("" /* displayId */,
- 540 /* sensorLocationX */,
- 1600 /* sensorLocationY */,
- 100 /* sensorRadius */)));
- }
-
- public class TestableView extends AuthBiometricFaceToFingerprintView {
- public TestableView(Context context) {
- super(context, null, new MockInjector());
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return 0;
- }
- }
-
- private class MockInjector extends AuthBiometricView.Injector {
- @Override
- public Button getNegativeButton() {
- return mNegativeButton;
- }
-
- @Override
- public Button getCancelButton() {
- return mCancelButton;
- }
-
- @Override
- public Button getUseCredentialButton() {
- return mUseCredentialButton;
- }
-
- @Override
- public Button getConfirmButton() {
- return mConfirmButton;
- }
-
- @Override
- public Button getTryAgainButton() {
- return mTryAgainButton;
- }
-
- @Override
- public TextView getTitleView() {
- return mTitleView;
- }
-
- @Override
- public TextView getSubtitleView() {
- return mSubtitleView;
- }
-
- @Override
- public TextView getDescriptionView() {
- return mDescriptionView;
- }
-
- @Override
- public TextView getIndicatorView() {
- return mIndicatorView;
- }
-
- @Override
- public ImageView getIconView() {
- return mIconView;
- }
-
- @Override
- public View getIconHolderView() {
- return mIconHolderView;
- }
-
- @Override
- public int getDelayAfterError() {
- return 0;
- }
-
- @Override
- public int getMediumToLargeAnimationDurationMs() {
- return 0;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
deleted file mode 100644
index b93381d2b5c9..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.biometrics;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import com.android.systemui.R;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class AuthBiometricFaceViewTest extends SysuiTestCase {
-
- @Mock
- AuthBiometricView.Callback mCallback;
-
- private TestableFaceView mFaceView;
-
- @Mock private Button mNegativeButton;
- @Mock private Button mCancelButton;
- @Mock private Button mUseCredentialButton;
-
- @Mock private Button mConfirmButton;
- @Mock private Button mTryAgainButton;
-
- @Mock private TextView mErrorView;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mFaceView = new TestableFaceView(mContext);
- mFaceView.mFaceIconController = mock(TestableFaceView.TestableIconController.class);
- mFaceView.setCallback(mCallback);
-
- mFaceView.mNegativeButton = mNegativeButton;
- mFaceView.mCancelButton = mCancelButton;
- mFaceView.mUseCredentialButton = mUseCredentialButton;
-
- mFaceView.mConfirmButton = mConfirmButton;
- mFaceView.mTryAgainButton = mTryAgainButton;
-
- mFaceView.mIndicatorView = mErrorView;
- }
-
- @Test
- public void testStateUpdated_whenDialogAnimatedIn() {
- mFaceView.onDialogAnimatedIn();
- verify(mFaceView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
- }
-
- @Test
- public void testIconUpdatesState_whenDialogStateUpdated() {
- mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING);
- verify(mFaceView.mFaceIconController)
- .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
-
- mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
- verify(mFaceView.mFaceIconController).updateState(
- eq(AuthBiometricFaceView.STATE_AUTHENTICATING),
- eq(AuthBiometricFaceView.STATE_AUTHENTICATED));
- }
-
- public class TestableFaceView extends AuthBiometricFaceView {
-
- public class TestableIconController extends IconController {
- TestableIconController(Context context, ImageView iconView) {
- super(context, iconView, mock(TextView.class));
- }
-
- public void startPulsing() {
- // Stub for testing
- }
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return 0; // Keep this at 0 for tests to invoke callback immediately.
- }
-
- public TestableFaceView(Context context) {
- super(context);
- }
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index f8e38e4994bc..9418b50ff390 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -20,137 +20,109 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static com.android.systemui.biometrics.AuthBiometricView.Callback.ACTION_AUTHENTICATED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.content.Context;
-import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.PromptInfo;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.AttributeSet;
+import android.testing.ViewUtils;
+import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+@Ignore
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class AuthBiometricViewTest extends SysuiTestCase {
- @Mock private AuthBiometricView.Callback mCallback;
- @Mock private AuthPanelController mPanelController;
-
- @Mock private Button mNegativeButton;
- @Mock private Button mCancelButton;
- @Mock private Button mUseCredentialButton;
-
- @Mock private Button mPositiveButton;
- @Mock private Button mTryAgainButton;
-
- @Mock private TextView mTitleView;
- @Mock private TextView mSubtitleView;
- @Mock private TextView mDescriptionView;
- @Mock private TextView mIndicatorView;
- @Mock private ImageView mIconView;
- @Mock private View mIconHolderView;
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
- private TestableBiometricView mBiometricView;
+ @Mock
+ private AuthBiometricView.Callback mCallback;
+ @Mock
+ private AuthPanelController mPanelController;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
+ private AuthBiometricView mBiometricView;
@Test
public void testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
// The onAuthenticated runnable is posted when authentication succeeds.
- mBiometricView.onAuthenticationSucceeded();
+ mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
waitForIdleSync();
assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED);
+ verify(mCallback).onAction(ACTION_AUTHENTICATED);
}
@Test
public void testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
- final Button negativeButton = new Button(mContext);
- final Button cancelButton = new Button(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getNegativeButton() {
- return negativeButton;
- }
-
- @Override
- public Button getCancelButton() {
- return cancelButton;
- }
- });
+ initDialog(false /* allowDeviceCredential */, mCallback);
mBiometricView.setRequireConfirmation(true);
- mBiometricView.onAuthenticationSucceeded();
+ mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
waitForIdleSync();
- assertEquals(AuthBiometricView.STATE_PENDING_CONFIRMATION, mBiometricView.mState);
- verify(mCallback, never()).onAction(anyInt());
- assertEquals(View.GONE, negativeButton.getVisibility());
- assertEquals(View.VISIBLE, cancelButton.getVisibility());
- assertTrue(cancelButton.isEnabled());
+ // TODO: this should be tested in the subclasses
+ if (mBiometricView.supportsRequireConfirmation()) {
+ assertEquals(AuthBiometricView.STATE_PENDING_CONFIRMATION, mBiometricView.mState);
+
+ verify(mCallback, never()).onAction(anyInt());
+
+ assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
+ assertEquals(View.VISIBLE, mBiometricView.mCancelButton.getVisibility());
+ assertTrue(mBiometricView.mCancelButton.isEnabled());
+
+ assertTrue(mBiometricView.mConfirmButton.isEnabled());
+ assertEquals(mContext.getText(R.string.biometric_dialog_tap_confirm),
+ mBiometricView.mIndicatorView.getText());
+ assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
+ } else {
+ assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
+ verify(mCallback).onAction(eq(ACTION_AUTHENTICATED));
+ }
- verify(mBiometricView.mConfirmButton).setEnabled(eq(true));
- verify(mIndicatorView).setText(eq(R.string.biometric_dialog_tap_confirm));
- verify(mIndicatorView).setVisibility(eq(View.VISIBLE));
}
@Test
public void testPositiveButton_sendsActionAuthenticated() {
- Button button = new Button(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getConfirmButton() {
- return button;
- }
- });
-
- button.performClick();
+ initDialog(false /* allowDeviceCredential */, mCallback);
+
+ mBiometricView.mConfirmButton.performClick();
waitForIdleSync();
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED);
+ verify(mCallback).onAction(ACTION_AUTHENTICATED);
assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
}
@Test
public void testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
- Button button = new Button(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getNegativeButton() {
- return button;
- }
- });
+ initDialog(false /* allowDeviceCredential */, mCallback);
mBiometricView.onDialogAnimatedIn();
- button.performClick();
+ mBiometricView.mNegativeButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
@@ -158,25 +130,14 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testCancelButton_whenPendingConfirmation_sendsActionUserCanceled() {
- Button cancelButton = new Button(mContext);
- Button negativeButton = new Button(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getNegativeButton() {
- return negativeButton;
- }
- @Override
- public Button getCancelButton() {
- return cancelButton;
- }
- });
+ initDialog(false /* allowDeviceCredential */, mCallback);
mBiometricView.setRequireConfirmation(true);
- mBiometricView.onAuthenticationSucceeded();
+ mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
- assertEquals(View.GONE, negativeButton.getVisibility());
+ assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
- cancelButton.performClick();
+ mBiometricView.mCancelButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED);
@@ -184,15 +145,9 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testTryAgainButton_sendsActionTryAgain() {
- Button button = new Button(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getTryAgainButton() {
- return button;
- }
- });
-
- button.performClick();
+ initDialog(false /* allowDeviceCredential */, mCallback);
+
+ mBiometricView.mTryAgainButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
@@ -202,7 +157,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
@Ignore("flaky, b/189031816")
public void testError_sendsActionError() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
final String testError = "testError";
mBiometricView.onError(TYPE_FACE, testError);
waitForIdleSync();
@@ -213,7 +168,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_sendsActionUserCanceled() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
@@ -223,18 +178,18 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
- mBiometricView.onAuthenticationSucceeded();
+ mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
view.performClick();
verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
}
@Test
public void testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
mBiometricView.mLayoutParams = new AuthDialog.LayoutParams(0, 0);
mBiometricView.updateSize(AuthDialog.SIZE_SMALL);
@@ -246,7 +201,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testIgnoresUselessHelp() {
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
+ initDialog(false /* allowDeviceCredential */, mCallback);
mBiometricView.onDialogAnimatedIn();
waitForIdleSync();
@@ -256,33 +211,16 @@ public class AuthBiometricViewTest extends SysuiTestCase {
mBiometricView.onHelp(TYPE_FINGERPRINT, "");
waitForIdleSync();
- verify(mIndicatorView, never()).setText(any());
+ assertEquals("", mBiometricView.mIndicatorView.getText());
verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR));
assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
}
@Test
public void testRestoresState() {
- final boolean requireConfirmation = true; // set/init from AuthController
-
- Button tryAgainButton = new Button(mContext);
- TextView indicatorView = new TextView(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getTryAgainButton() {
- return tryAgainButton;
- }
- @Override
- public TextView getIndicatorView() {
- return indicatorView;
- }
-
- @Override
- public int getDelayAfterError() {
- // keep a real delay to test saving in the error state
- return BiometricPrompt.HIDE_DIALOG_DELAY;
- }
- });
+ final boolean requireConfirmation = true;
+
+ initDialog(false /* allowDeviceCredential */, mCallback, null, 10000);
final String failureMessage = "testFailureMessage";
mBiometricView.setRequireConfirmation(requireConfirmation);
@@ -292,8 +230,8 @@ public class AuthBiometricViewTest extends SysuiTestCase {
Bundle state = new Bundle();
mBiometricView.onSaveState(state);
- assertEquals(View.VISIBLE, tryAgainButton.getVisibility());
- assertEquals(View.VISIBLE, state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
+ assertEquals(View.GONE, mBiometricView.mTryAgainButton.getVisibility());
+ assertEquals(View.GONE, state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
assertEquals(AuthBiometricView.STATE_ERROR, state.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
@@ -307,25 +245,12 @@ public class AuthBiometricViewTest extends SysuiTestCase {
// TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle
// Create new dialog and restore the previous state into it
- Button tryAgainButton2 = new Button(mContext);
- TextView indicatorView2 = new TextView(mContext);
- initDialog(mContext, false /* allowDeviceCredential */, mCallback, state,
- new MockInjector() {
- @Override
- public Button getTryAgainButton() {
- return tryAgainButton2;
- }
-
- @Override
- public TextView getIndicatorView() {
- return indicatorView2;
- }
- });
+ initDialog(false /* allowDeviceCredential */, mCallback, state, 10000);
+ mBiometricView.mAnimationDurationHideDialog = 10000;
mBiometricView.setRequireConfirmation(requireConfirmation);
waitForIdleSync();
- // Test restored state
- assertEquals(View.VISIBLE, tryAgainButton.getVisibility());
+ assertEquals(View.GONE, mBiometricView.mTryAgainButton.getVisibility());
assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
@@ -334,23 +259,12 @@ public class AuthBiometricViewTest extends SysuiTestCase {
}
@Test
- public void testCredentialButton_whenDeviceCredentialAllowed() {
- final Button negativeButton = new Button(mContext);
- final Button useCredentialButton = new Button(mContext);
- initDialog(mContext, true /* allowDeviceCredential */, mCallback, new MockInjector() {
- @Override
- public Button getNegativeButton() {
- return negativeButton;
- }
-
- @Override
- public Button getUseCredentialButton() {
- return useCredentialButton;
- }
- });
-
- assertEquals(View.GONE, negativeButton.getVisibility());
- useCredentialButton.performClick();
+ public void testCredentialButton_whenDeviceCredentialAllowed() throws InterruptedException {
+ initDialog(true /* allowDeviceCredential */, mCallback);
+
+ assertEquals(View.VISIBLE, mBiometricView.mUseCredentialButton.getVisibility());
+ assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
+ mBiometricView.mUseCredentialButton.performClick();
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
@@ -369,120 +283,30 @@ public class AuthBiometricViewTest extends SysuiTestCase {
return promptInfo;
}
- private void initDialog(Context context, boolean allowDeviceCredential,
- AuthBiometricView.Callback callback,
- Bundle savedState, MockInjector injector) {
- mBiometricView = new TestableBiometricView(context, null, injector);
+ private void initDialog(boolean allowDeviceCredential, AuthBiometricView.Callback callback) {
+ initDialog(allowDeviceCredential, callback,
+ null /* savedState */, 0 /* hideDelay */);
+ }
+
+ private void initDialog(boolean allowDeviceCredential,
+ AuthBiometricView.Callback callback, Bundle savedState, int hideDelay) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ mBiometricView = (AuthBiometricView) inflater.inflate(
+ R.layout.auth_biometric_view, null, false);
+ mBiometricView.mAnimationDurationLong = 0;
+ mBiometricView.mAnimationDurationShort = 0;
+ mBiometricView.mAnimationDurationHideDialog = hideDelay;
mBiometricView.setPromptInfo(buildPromptInfo(allowDeviceCredential));
mBiometricView.setCallback(callback);
mBiometricView.restoreState(savedState);
- mBiometricView.onFinishInflateInternal();
- mBiometricView.onAttachedToWindowInternal();
-
+ ViewUtils.attachView(mBiometricView);
mBiometricView.setPanelController(mPanelController);
+ waitForIdleSync();
}
- private void initDialog(Context context, boolean allowDeviceCredential,
- AuthBiometricView.Callback callback, MockInjector injector) {
- initDialog(context, allowDeviceCredential, callback, null /* savedState */, injector);
- }
-
- private class MockInjector extends AuthBiometricView.Injector {
- @Override
- public Button getNegativeButton() {
- return mNegativeButton;
- }
-
- @Override
- public Button getCancelButton() {
- return mCancelButton;
- }
-
- @Override
- public Button getUseCredentialButton() {
- return mUseCredentialButton;
- }
-
- @Override
- public Button getConfirmButton() {
- return mPositiveButton;
- }
-
- @Override
- public Button getTryAgainButton() {
- return mTryAgainButton;
- }
-
- @Override
- public TextView getTitleView() {
- return mTitleView;
- }
-
- @Override
- public TextView getSubtitleView() {
- return mSubtitleView;
- }
-
- @Override
- public TextView getDescriptionView() {
- return mDescriptionView;
- }
-
- @Override
- public TextView getIndicatorView() {
- return mIndicatorView;
- }
-
- @Override
- public ImageView getIconView() {
- return mIconView;
- }
-
- @Override
- public View getIconHolderView() {
- return mIconHolderView;
- }
-
- @Override
- public int getDelayAfterError() {
- return 0; // Keep this at 0 for tests to invoke callback immediately.
- }
-
- @Override
- public int getMediumToLargeAnimationDurationMs() {
- return 0;
- }
- }
-
- private class TestableBiometricView extends AuthBiometricView {
- TestableBiometricView(Context context, AttributeSet attrs,
- Injector injector) {
- super(context, attrs, injector);
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return 0; // Keep this at 0 for tests to invoke callback immediately.
- }
-
- @Override
- protected int getStateForAfterError() {
- return 0;
- }
-
- @Override
- protected void handleResetAfterError() {
-
- }
-
- @Override
- protected void handleResetAfterHelp() {
-
- }
-
- @Override
- protected boolean supportsSmallDialog() {
- return false;
- }
+ @Override
+ protected void waitForIdleSync() {
+ TestableLooper.get(this).processAllMessages();
+ super.waitForIdleSync();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
deleted file mode 100644
index ae1268d48af9..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * 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.biometrics;
-
-import static android.hardware.biometrics.BiometricManager.Authenticators;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.PromptInfo;
-import android.hardware.biometrics.SensorProperties;
-import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.IBinder;
-import android.os.UserManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ScrollView;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class AuthContainerViewTest extends SysuiTestCase {
-
- private TestableAuthContainer mAuthContainer;
-
- private @Mock AuthDialogCallback mCallback;
- private @Mock UserManager mUserManager;
- private @Mock WakefulnessLifecycle mWakefulnessLifecycle;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testActionAuthenticated_sendsDismissedAuthenticated() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_AUTHENTICATED);
- verify(mCallback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
- eq(null) /* credentialAttestation */);
- }
-
- @Test
- public void testActionUserCanceled_sendsDismissedUserCanceled() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_USER_CANCELED);
- verify(mCallback).onSystemEvent(eq(
- BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL));
- verify(mCallback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
- eq(null) /* credentialAttestation */);
- }
-
- @Test
- public void testActionButtonNegative_sendsDismissedButtonNegative() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
- verify(mCallback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
- eq(null) /* credentialAttestation */);
- }
-
- @Test
- public void testActionTryAgain_sendsTryAgain() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
- verify(mCallback).onTryAgainPressed();
- }
-
- @Test
- public void testActionError_sendsDismissedError() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_ERROR);
- verify(mCallback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_ERROR),
- eq(null) /* credentialAttestation */);
- }
-
- @Test
- public void testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
- initializeContainer(
- Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL);
-
- mAuthContainer.mBiometricCallback.onAction(
- AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
- verify(mCallback).onDeviceCredentialPressed();
-
- // Credential view is attached to the frame layout
- waitForIdleSync();
- assertNotNull(mAuthContainer.mCredentialView);
- verify(mAuthContainer.mFrameLayout).addView(eq(mAuthContainer.mCredentialView));
- }
-
- @Test
- public void testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
- initializeContainer(
- Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL);
-
- mAuthContainer.mBiometricView = mock(AuthBiometricView.class);
- mAuthContainer.animateToCredentialUI();
- verify(mAuthContainer.mBiometricView).startTransitionToCredentialUI();
- }
-
- @Test
- public void testShowBiometricUI() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
-
- assertNotEquals(null, mAuthContainer.mBiometricView);
-
- mAuthContainer.onAttachedToWindowInternal();
- verify(mAuthContainer.mBiometricScrollView).addView(mAuthContainer.mBiometricView);
- // Credential view is not added
- verify(mAuthContainer.mFrameLayout, never()).addView(any());
- }
-
- @Test
- public void testShowCredentialUI_doesNotInflateBiometricUI() {
- initializeContainer(Authenticators.DEVICE_CREDENTIAL);
-
- mAuthContainer.onAttachedToWindowInternal();
-
- assertNull(null, mAuthContainer.mBiometricView);
- assertNotNull(mAuthContainer.mCredentialView);
- verify(mAuthContainer.mFrameLayout).addView(mAuthContainer.mCredentialView);
- }
-
- @Test
- public void testCredentialViewUsesEffectiveUserId() {
- final int dummyEffectiveUserId = 200;
- when(mUserManager.getCredentialOwnerProfile(anyInt())).thenReturn(dummyEffectiveUserId);
-
- initializeContainer(Authenticators.DEVICE_CREDENTIAL);
- mAuthContainer.onAttachedToWindowInternal();
- assertTrue(mAuthContainer.mCredentialView instanceof AuthCredentialPatternView);
- assertEquals(dummyEffectiveUserId, mAuthContainer.mCredentialView.mEffectiveUserId);
- assertEquals(Utils.CREDENTIAL_PATTERN, mAuthContainer.mCredentialView.mCredentialType);
- }
-
- @Test
- public void testCredentialUI_disablesClickingOnBackground() {
- // In the credential view, clicking on the background (to cancel authentication) is not
- // valid. Thus, the listener should be null, and it should not be in the accessibility
- // hierarchy.
- initializeContainer(Authenticators.DEVICE_CREDENTIAL);
-
- mAuthContainer.onAttachedToWindowInternal();
-
- verify(mAuthContainer.mBackgroundView).setOnClickListener(eq(null));
- verify(mAuthContainer.mBackgroundView).setImportantForAccessibility(
- eq(View.IMPORTANT_FOR_ACCESSIBILITY_NO));
- }
-
- @Test
- public void testOnDialogAnimatedIn_sendsCancelReason_whenPendingDismiss() {
- initializeContainer(Authenticators.BIOMETRIC_WEAK);
- mAuthContainer.mContainerState = AuthContainerView.STATE_PENDING_DISMISS;
- mAuthContainer.onDialogAnimatedIn();
- verify(mCallback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
- eq(null) /* credentialAttestation */);
- }
-
- @Test
- public void testLayoutParams_hasSecureWindowFlag() {
- final IBinder windowToken = mock(IBinder.class);
- final WindowManager.LayoutParams layoutParams =
- AuthContainerView.getLayoutParams(windowToken, "");
- assertTrue((layoutParams.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0);
- }
-
- @Test
- public void testLayoutParams_excludesImeInsets() {
- final IBinder windowToken = mock(IBinder.class);
- final WindowManager.LayoutParams layoutParams =
- AuthContainerView.getLayoutParams(windowToken, "");
- assertTrue((layoutParams.getFitInsetsTypes() & WindowInsets.Type.ime()) == 0);
- }
-
- private void initializeContainer(int authenticators) {
- AuthContainerView.Config config = new AuthContainerView.Config();
- config.mContext = mContext;
- config.mCallback = mCallback;
- config.mSensorIds = new int[] {0};
- config.mCredentialAllowed = false;
-
- PromptInfo promptInfo = new PromptInfo();
- promptInfo.setAuthenticators(authenticators);
- config.mPromptInfo = promptInfo;
-
- final List<FingerprintSensorPropertiesInternal> fpProps = new ArrayList<>();
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */));
- componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */));
-
- fpProps.add(new FingerprintSensorPropertiesInternal(0,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_REAR,
- false /* resetLockoutRequiresHardwareAuthToken */));
- mAuthContainer = new TestableAuthContainer(config, fpProps, null /* faceProps */,
- mWakefulnessLifecycle);
- }
-
- private class TestableAuthContainer extends AuthContainerView {
- TestableAuthContainer(AuthContainerView.Config config,
- @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
- @Nullable List<FaceSensorPropertiesInternal> faceProps,
- WakefulnessLifecycle wakefulnessLifecycle) {
-
- super(config, new MockInjector(), fpProps, faceProps, wakefulnessLifecycle);
- }
-
- @Override
- public void animateAway(int reason) {
- // TODO: Credential attestation should be testable/tested
- mConfig.mCallback.onDismissed(reason, null /* credentialAttestation */);
- }
- }
-
- private final class MockInjector extends AuthContainerView.Injector {
- @Override
- public ScrollView getBiometricScrollView(FrameLayout parent) {
- return mock(ScrollView.class);
- }
-
- @Override
- public FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
- return mock(FrameLayout.class);
- }
-
- @Override
- public AuthPanelController getPanelController(Context context, View view) {
- return mock(AuthPanelController.class);
- }
-
- @Override
- public ImageView getBackgroundView(FrameLayout parent) {
- return mock(ImageView.class);
- }
-
- @Override
- public View getPanelView(FrameLayout parent) {
- return mock(View.class);
- }
-
- @Override
- public int getAnimateCredentialStartDelayMs() {
- return 0;
- }
-
- @Override
- public UserManager getUserManager(Context context) {
- return mUserManager;
- }
-
- @Override
- public @Utils.CredentialType int getCredentialType(Context context, int effectiveUserId) {
- return Utils.CREDENTIAL_PATTERN;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
new file mode 100644
index 000000000000..6f0a8a6adfef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.biometrics
+
+import android.app.admin.DevicePolicyManager
+import android.hardware.biometrics.BiometricConstants
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.SensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintSensorProperties
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Handler
+import android.os.IBinder
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.testing.ViewUtils
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.widget.ScrollView
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@Ignore
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class AuthContainerViewTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ var rule = MockitoJUnit.rule()
+
+ @Mock
+ lateinit var callback: AuthDialogCallback
+ @Mock
+ lateinit var userManager: UserManager
+ @Mock
+ lateinit var lockPatternUtils: LockPatternUtils
+ @Mock
+ lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock
+ lateinit var windowToken: IBinder
+
+ private lateinit var authContainer: TestAuthContainerView
+
+ @Test
+ fun testActionAuthenticated_sendsDismissedAuthenticated() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_AUTHENTICATED
+ )
+ waitForIdleSync()
+
+ verify(callback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
+ eq<ByteArray?>(null) /* credentialAttestation */
+ )
+ assertThat(authContainer.parent).isNull()
+ }
+
+ @Test
+ fun testActionUserCanceled_sendsDismissedUserCanceled() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_USER_CANCELED
+ )
+ waitForIdleSync()
+
+ verify(callback).onSystemEvent(
+ eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL)
+ )
+ verify(callback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null) /* credentialAttestation */
+ )
+ assertThat(authContainer.parent).isNull()
+ }
+
+ @Test
+ fun testActionButtonNegative_sendsDismissedButtonNegative() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE
+ )
+ waitForIdleSync()
+
+ verify(callback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
+ eq<ByteArray?>(null) /* credentialAttestation */
+ )
+ assertThat(authContainer.parent).isNull()
+ }
+
+ @Test
+ fun testActionTryAgain_sendsTryAgain() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN
+ )
+ waitForIdleSync()
+
+ verify(callback).onTryAgainPressed()
+ }
+
+ @Test
+ fun testActionError_sendsDismissedError() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_ERROR
+ )
+ waitForIdleSync()
+
+ verify(callback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_ERROR),
+ eq<ByteArray?>(null) /* credentialAttestation */
+ )
+ assertThat(authContainer.parent).isNull()
+ }
+
+ @Test
+ fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
+ initializeContainer(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
+ authContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL
+ )
+ waitForIdleSync()
+
+ verify(callback).onDeviceCredentialPressed()
+ assertThat(authContainer.hasCredentialView()).isTrue()
+ }
+
+ @Test
+ fun testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
+ initializeContainer(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
+ authContainer.animateToCredentialUI()
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialView()).isTrue()
+ }
+
+ @Test
+ fun testShowBiometricUI() {
+ initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialView()).isFalse()
+ assertThat(authContainer.hasBiometricPrompt()).isTrue()
+ }
+
+ @Test
+ fun testShowCredentialUI() {
+ initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialView()).isTrue()
+ assertThat(authContainer.hasBiometricPrompt()).isFalse()
+ }
+
+ @Test
+ fun testCredentialViewUsesEffectiveUserId() {
+ whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200)
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn(
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+ )
+
+ initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialPatternView()).isTrue()
+ assertThat(authContainer.hasBiometricPrompt()).isFalse()
+ }
+
+ @Test
+ fun testCredentialUI_disablesClickingOnBackground() {
+ whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ )
+
+ // In the credential view, clicking on the background (to cancel authentication) is not
+ // valid. Thus, the listener should be null, and it should not be in the accessibility
+ // hierarchy.
+ initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialPasswordView()).isTrue()
+ assertThat(authContainer.hasBiometricPrompt()).isFalse()
+ assertThat(
+ authContainer.findViewById<View>(R.id.background)?.isImportantForAccessibility
+ ).isFalse()
+
+ authContainer.findViewById<View>(R.id.background)?.performClick()
+ waitForIdleSync()
+
+ assertThat(authContainer.hasCredentialPasswordView()).isTrue()
+ assertThat(authContainer.hasBiometricPrompt()).isFalse()
+ }
+
+ @Test
+ fun testLayoutParams_hasSecureWindowFlag() {
+ val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
+ assertThat((layoutParams.flags and WindowManager.LayoutParams.FLAG_SECURE) != 0).isTrue()
+ }
+
+ @Test
+ fun testLayoutParams_excludesImeInsets() {
+ val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
+ assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.ime()) == 0).isTrue()
+ }
+
+ private fun initializeContainer(authenticators: Int) {
+ val config = AuthContainerView.Config()
+ config.mContext = mContext
+ config.mCallback = callback
+ config.mSensorIds = intArrayOf(0)
+ config.mSkipAnimation = true
+ config.mPromptInfo = PromptInfo()
+ config.mPromptInfo.authenticators = authenticators
+ val componentInfo = listOf(
+ ComponentInfoInternal(
+ "faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */
+ ),
+ ComponentInfoInternal(
+ "matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */
+ )
+ )
+ val fpProps = listOf(
+ FingerprintSensorPropertiesInternal(
+ 0,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ componentInfo,
+ FingerprintSensorProperties.TYPE_REAR,
+ false /* resetLockoutRequiresHardwareAuthToken */
+ )
+ )
+ authContainer = TestAuthContainerView(
+ config,
+ fpProps,
+ listOf(),
+ wakefulnessLifecycle,
+ userManager,
+ lockPatternUtils,
+ Handler(TestableLooper.get(this).looper)
+ )
+ ViewUtils.attachView(authContainer)
+ }
+
+ private inner class TestAuthContainerView(
+ config: Config,
+ fpProps: List<FingerprintSensorPropertiesInternal>,
+ faceProps: List<FaceSensorPropertiesInternal>,
+ wakefulnessLifecycle: WakefulnessLifecycle,
+ userManager: UserManager,
+ lockPatternUtils: LockPatternUtils,
+ mainHandler: Handler
+ ) : AuthContainerView(
+ config, fpProps, faceProps,
+ wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler
+ ) {
+ override fun postOnAnimation(runnable: Runnable) {
+ runnable.run()
+ }
+ }
+
+ override fun waitForIdleSync() {
+ TestableLooper.get(this).processAllMessages()
+ super.waitForIdleSync()
+ }
+}
+
+private fun AuthContainerView.hasBiometricPrompt() =
+ (findViewById<ScrollView>(R.id.biometric_scrollview)?.childCount ?: 0) > 0
+
+private fun AuthContainerView.hasCredentialView() =
+ hasCredentialPatternView() || hasCredentialPasswordView()
+
+private fun AuthContainerView.hasCredentialPatternView() =
+ findViewById<View>(R.id.lockPattern) != null
+
+private fun AuthContainerView.hasCredentialPasswordView() =
+ findViewById<View>(R.id.lockPassword) != null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index c37e966f3540..cfac96512582 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -16,8 +16,9 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
-import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
@@ -62,6 +63,7 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
@@ -71,6 +73,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -79,6 +82,8 @@ import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
@@ -86,7 +91,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
@@ -94,11 +100,15 @@ import java.util.Random;
import javax.inject.Provider;
+@Ignore
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class AuthControllerTest extends SysuiTestCase {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
@Mock
private PackageManager mPackageManager;
@Mock
@@ -128,6 +138,10 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
+ private UserManager mUserManager;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Captor
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
@@ -144,8 +158,6 @@ public class AuthControllerTest extends SysuiTestCase {
@Before
public void setup() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
mContextSpy = spy(mContext);
mExecution = new FakeExecution();
mTestableLooper = TestableLooper.get(this);
@@ -343,8 +355,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Test
public void testOnAuthenticationSucceededInvoked_whenSystemRequested() {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
- mAuthController.onBiometricAuthenticated();
- verify(mDialog1).onAuthenticationSucceeded();
+ mAuthController.onBiometricAuthenticated(TYPE_FINGERPRINT);
+ verify(mDialog1).onAuthenticationSucceeded(eq(TYPE_FINGERPRINT));
}
@Test
@@ -528,8 +540,7 @@ public class AuthControllerTest extends SysuiTestCase {
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
Bundle savedState = (Bundle) args[0];
- savedState.putInt(
- AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false);
return null; // onSaveState returns void
}).when(mDialog1).onSaveState(any());
@@ -558,8 +569,7 @@ public class AuthControllerTest extends SysuiTestCase {
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
Bundle savedState = (Bundle) args[0];
- savedState.putInt(
- AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false);
savedState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, true);
return null; // onSaveState returns void
}).when(mDialog1).onSaveState(any());
@@ -697,7 +707,7 @@ public class AuthControllerTest extends SysuiTestCase {
0 /* operationId */,
"testPackage",
1 /* requestId */,
- BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT);
+ BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE);
}
private PromptInfo createTestPromptInfo() {
@@ -739,15 +749,16 @@ public class AuthControllerTest extends SysuiTestCase {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
- statusBarStateController, mHandler);
+ mUserManager, mLockPatternUtils, statusBarStateController, mHandler);
}
@Override
protected AuthDialog buildDialog(PromptInfo promptInfo,
- boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed,
+ boolean requireConfirmation, int userId, int[] sensorIds,
String opPackageName, boolean skipIntro, long operationId, long requestId,
@BiometricManager.BiometricMultiSensorMode int multiSensorConfig,
- WakefulnessLifecycle wakefulnessLifecycle) {
+ WakefulnessLifecycle wakefulnessLifecycle, UserManager userManager,
+ LockPatternUtils lockPatternUtils) {
mLastBiometricPromptInfo = promptInfo;
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 5128ccc15d9d..ec2c1de49b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -32,7 +32,7 @@ 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.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
@@ -61,7 +61,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
private lateinit var staticMockSession: MockitoSession
private lateinit var controller: AuthRippleController
- @Mock private lateinit var statusBar: StatusBar
+ @Mock private lateinit var mCentralSurfaces: CentralSurfaces
@Mock private lateinit var rippleView: AuthRippleView
@Mock private lateinit var commandRegistry: CommandRegistry
@Mock private lateinit var configurationController: ConfigurationController
@@ -89,7 +89,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
- statusBar,
+ mCentralSurfaces,
context,
authController,
configurationController,
@@ -105,7 +105,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
rippleView
)
controller.init()
- `when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
+ `when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim)
}
@After
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 254fc5945522..839c0ab1318f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.biometrics
import android.animation.Animator
-import android.graphics.Insets
import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.content.ComponentName
+import android.graphics.Insets
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
@@ -65,8 +65,8 @@ import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
-import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 066a866118dd..ef82c3ec3322 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -57,9 +57,9 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
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 35e838bfca9a..1856fda14e1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -65,7 +65,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -121,7 +121,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
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 7fb42b6ea11e..186f2bb1d127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -124,6 +124,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
when(mView.getContext()).thenReturn(mResourceContext);
when(mResourceContext.getString(anyInt())).thenReturn("test string");
when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
+ when(mView.getDialogSuggestedAlpha()).thenReturn(1f);
mController = new UdfpsKeyguardViewController(
mView,
mStatusBarStateController,
@@ -144,7 +145,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
@Test
public void testRegistersExpansionChangedListenerOnAttached() {
mController.onViewAttached();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
}
@Test
@@ -173,7 +174,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testListenersUnregisteredOnDetached() {
mController.onViewAttached();
captureStatusBarStateListeners();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
captureKeyguardStateControllerCallback();
mController.onViewDetached();
@@ -200,10 +201,41 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testShouldPauseAuthBouncerShowing() {
mController.onViewAttached();
captureStatusBarStateListeners();
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+ captureAltAuthInterceptor();
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ mAltAuthInterceptor.onBouncerVisibilityChanged();
+ assertTrue(mController.shouldPauseAuth());
+ }
+
+ @Test
+ public void testShouldPauseAuthDialogSuggestedAlpha0() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+
+ when(mView.getDialogSuggestedAlpha()).thenReturn(0f);
sendStatusBarStateChanged(StatusBarState.KEYGUARD);
- assertFalse(mController.shouldPauseAuth());
+ assertTrue(mController.shouldPauseAuth());
+ }
+
+ @Test
+ public void testFadeFromDialogSuggestedAlpha() {
+ // GIVEN view is attached and status bar expansion is 1f
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureStatusBarExpansionListeners();
+ updateStatusBarExpansion(1f, true);
+ reset(mView);
+
+ // WHEN dialog suggested alpha is .6f
+ when(mView.getDialogSuggestedAlpha()).thenReturn(.6f);
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+ // THEN alpha is updated based on dialog suggested alpha
+ verify(mView).setUnpausedAlpha((int) (.6f * 255));
}
@Test
@@ -367,7 +399,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testFadeInWithStatusBarExpansion() {
// GIVEN view is attached
mController.onViewAttached();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
captureKeyguardStateControllerCallback();
reset(mView);
@@ -382,7 +414,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testShowUdfpsBouncer() {
// GIVEN view is attached and status bar expansion is 0
mController.onViewAttached();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
captureKeyguardStateControllerCallback();
captureAltAuthInterceptor();
updateStatusBarExpansion(0, true);
@@ -401,9 +433,10 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testTransitionToFullShadeProgress() {
// GIVEN view is attached and status bar expansion is 1f
mController.onViewAttached();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
updateStatusBarExpansion(1f, true);
reset(mView);
+ when(mView.getDialogSuggestedAlpha()).thenReturn(1f);
// WHEN we're transitioning to the full shade
float transitionProgress = .6f;
@@ -417,7 +450,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
public void testShowUdfpsBouncer_transitionToFullShadeProgress() {
// GIVEN view is attached and status bar expansion is 1f
mController.onViewAttached();
- captureExpansionListeners();
+ captureStatusBarExpansionListeners();
captureKeyguardStateControllerCallback();
captureAltAuthInterceptor();
updateStatusBarExpansion(1f, true);
@@ -440,7 +473,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mStatusBarStateListener = mStateListenerCaptor.getValue();
}
- private void captureExpansionListeners() {
+ private void captureStatusBarExpansionListeners() {
verify(mPanelExpansionStateManager, times(2))
.addExpansionListener(mExpansionListenerCaptor.capture());
// first (index=0) is from super class, UdfpsAnimationViewController.
@@ -460,8 +493,6 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
}
-
-
private void captureKeyguardStateControllerCallback() {
verify(mKeyguardStateController).addCallback(
mKeyguardStateControllerCallbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 2cd470e49d0c..3d8d1282e184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -38,11 +38,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.nullable
import org.mockito.Mockito.never
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
private const val DISPLAY_ID = "" // default display id
private const val SENSOR_X = 50
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index 12096bc06748..af3f24a1c58a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -210,4 +210,25 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
eq(actionCallbackService))
assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
}
+
+ @Test
+ fun testFalseBindCallsUnbind() {
+ val falseContext = mock(Context::class.java)
+ `when`(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false)
+ val manager = ControlsProviderLifecycleManager(
+ falseContext,
+ executor,
+ actionCallbackService,
+ UserHandle.of(0),
+ componentName
+ )
+ manager.bindService()
+ executor.runAllReady()
+
+ val captor = ArgumentCaptor.forClass(
+ ServiceConnection::class.java
+ )
+ verify(falseContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any())
+ verify(falseContext).unbindService(captor.value)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 56844292ce4a..3340f2fd5749 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -143,8 +143,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnDisabled_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
mMachine.requestState(INITIALIZED);
@@ -154,8 +154,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnEnabled_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -165,8 +165,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testInitialize_dozeSuppressed_afterDocked_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_afterDocked_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -176,8 +176,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
@@ -189,8 +189,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
@@ -228,8 +228,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testPulseDone_dozeSuppressed_goesToSuppressed() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_goesToSuppressed() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
@@ -266,8 +266,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testPulseDone_dozeSuppressed_afterDocked_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_afterDocked_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
mMachine.requestState(INITIALIZED);
mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
@@ -295,8 +295,8 @@ public class DozeMachineTest extends SysuiTestCase {
}
@Test
- public void testPulseDone_dozeSuppressed_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
new file mode 100644
index 000000000000..aa0a909622bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions andatest
+ * limitations under the License.
+ */
+
+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.FINISH;
+import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
+import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.res.Configuration;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class DozeSuppressorTest extends SysuiTestCase {
+
+ DozeSuppressor mDozeSuppressor;
+ @Mock
+ private DozeLog mDozeLog;
+ @Mock
+ private DozeHost mDozeHost;
+ @Mock
+ private AmbientDisplayConfiguration mConfig;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private UiModeManager mUiModeManager;
+ @Mock
+ private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ @Mock
+ private BiometricUnlockController mBiometricUnlockController;
+
+ @Mock
+ private DozeMachine mDozeMachine;
+
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ private BroadcastReceiver mBroadcastReceiver;
+
+ @Captor
+ private ArgumentCaptor<DozeHost.Callback> mDozeHostCaptor;
+ private DozeHost.Callback mDozeHostCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // setup state for NOT ending doze immediately
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ when(mBiometricUnlockController.hasPendingAuthentication()).thenReturn(false);
+ when(mDozeHost.isProvisioned()).thenReturn(true);
+
+ mDozeSuppressor = new DozeSuppressor(
+ mDozeHost,
+ mConfig,
+ mDozeLog,
+ mBroadcastDispatcher,
+ mUiModeManager,
+ mBiometricUnlockControllerLazy);
+
+ mDozeSuppressor.setDozeMachine(mDozeMachine);
+ }
+
+ @After
+ public void tearDown() {
+ mDozeSuppressor.destroy();
+ }
+
+ @Test
+ public void testRegistersListenersOnInitialized_unregisteredOnFinish() {
+ // check that receivers and callbacks registered
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureBroadcastReceiver();
+ captureDozeHostCallback();
+
+ // check that receivers and callbacks are unregistered
+ mDozeSuppressor.transitionTo(INITIALIZED, FINISH);
+ verify(mBroadcastDispatcher).unregisterReceiver(mBroadcastReceiver);
+ verify(mDozeHost).removeCallback(mDozeHostCallback);
+ }
+
+ @Test
+ public void testEndDoze_carMode() {
+ // GIVEN car mode
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testEndDoze_unprovisioned() {
+ // GIVEN device unprovisioned
+ when(mDozeHost.isProvisioned()).thenReturn(false);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testEndDoze_hasPendingUnlock() {
+ // GIVEN device unprovisioned
+ when(mBiometricUnlockController.hasPendingAuthentication()).thenReturn(true);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testPowerSaveChanged_active() {
+ // GIVEN AOD power save is active and doze is initialized
+ when(mDozeHost.isPowerSaveActive()).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureDozeHostCallback();
+
+ // WHEN power save change gets triggered (even if active = false, since it
+ // may differ from the aodPowerSaveActive state reported by DostHost)
+ mDozeHostCallback.onPowerSaveChanged(false);
+
+ // THEN the state changes to DOZE
+ verify(mDozeMachine).requestState(DOZE);
+ }
+
+ @Test
+ public void testPowerSaveChanged_notActive() {
+ // GIVEN DOZE (not showing aod content)
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE);
+ captureDozeHostCallback();
+
+ // WHEN power save mode is no longer active
+ when(mDozeHost.isPowerSaveActive()).thenReturn(false);
+ mDozeHostCallback.onPowerSaveChanged(false);
+
+ // THEN the state changes to DOZE_AOD
+ verify(mDozeMachine).requestState(DOZE_AOD);
+ }
+
+ @Test
+ public void testAlwaysOnSuppressedChanged_nowSuppressed() {
+ // GIVEN DOZE_AOD
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE_AOD);
+ captureDozeHostCallback();
+
+ // WHEN alwaysOnSuppressedChanged to suppressed=true
+ mDozeHostCallback.onAlwaysOnSuppressedChanged(true);
+
+ // THEN DOZE requested
+ verify(mDozeMachine).requestState(DOZE);
+ }
+
+ @Test
+ public void testAlwaysOnSuppressedChanged_notSuppressed() {
+ // GIVEN DOZE
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE);
+ captureDozeHostCallback();
+
+ // WHEN alwaysOnSuppressedChanged to suppressed=false
+ mDozeHostCallback.onAlwaysOnSuppressedChanged(false);
+
+ // THEN DOZE_AOD requested
+ verify(mDozeMachine).requestState(DOZE_AOD);
+ }
+
+ private void captureDozeHostCallback() {
+ verify(mDozeHost).addCallback(mDozeHostCaptor.capture());
+ mDozeHostCallback = mDozeHostCaptor.getValue();
+ }
+
+ private void captureBroadcastReceiver() {
+ verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ anyObject());
+ mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 21768edd40e9..b3d54590dc99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -28,6 +28,7 @@ import android.service.dreams.DreamService;
import android.service.dreams.IDreamOverlay;
import android.service.dreams.IDreamOverlayCallback;
import android.testing.AndroidTestingRunner;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -99,6 +100,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamPreviewComplication mPreviewComplication;
+ @Mock
+ ViewGroup mDreamOverlayContainerViewParent;
+
DreamOverlayService mService;
@Before
@@ -152,19 +156,36 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
}
@Test
- public void testShouldShowComplicationsTrueByDefault() {
+ public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized()
+ throws Exception {
+ when(mDreamOverlayContainerView.getParent())
+ .thenReturn(mDreamOverlayContainerViewParent)
+ .thenReturn(null);
+
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView);
+ }
+
+ @Test
+ public void testShouldShowComplicationsFalseByDefault() {
mService.onBind(new Intent());
- assertThat(mService.shouldShowComplications()).isTrue();
+ assertThat(mService.shouldShowComplications()).isFalse();
}
@Test
public void testShouldShowComplicationsSetByIntentExtra() {
final Intent intent = new Intent();
- intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, false);
+ intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true);
mService.onBind(intent);
- assertThat(mService.shouldShowComplications()).isFalse();
+ assertThat(mService.shouldShowComplications()).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 49da4bd5a825..3ce9889571f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -158,6 +158,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
public void testComplicationFilteringWhenShouldShowComplications() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
+ stateController.setShouldShowComplications(true);
final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
final Complication weatherComplication = Mockito.mock(Complication.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index ad8d44d62a5c..a32ff801e824 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -17,19 +17,34 @@
package com.android.systemui.dreams;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
+import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.touch.TouchInsetManager;
+import com.android.systemui.util.time.DateFormatUtil;
import org.junit.Before;
import org.junit.Test;
@@ -41,6 +56,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+ private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
+ "{count, plural, =1 {# notification} other {# notifications}}";
@Mock
DreamOverlayStatusBarView mView;
@@ -52,14 +69,55 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
Network mNetwork;
@Mock
TouchInsetManager.TouchInsetSession mTouchSession;
+ @Mock
+ Resources mResources;
+ @Mock
+ AlarmManager mAlarmManager;
+ @Mock
+ AlarmManager.AlarmClockInfo mAlarmClockInfo;
+ @Mock
+ NextAlarmController mNextAlarmController;
+ @Mock
+ DateFormatUtil mDateFormatUtil;
+ @Mock
+ IndividualSensorPrivacyController mSensorPrivacyController;
+ @Mock
+ StatusBarNotification mStatusBarNotification;
+ @Mock
+ NotificationListenerService.RankingMap mRankingMap;
+ @Mock
+ NotificationListener mNotificationListener;
+ @Mock
+ ZenModeController mZenModeController;
DreamOverlayStatusBarViewController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager,
- mTouchSession);
+
+ when(mResources.getString(R.string.dream_overlay_status_bar_notification_indicator))
+ .thenReturn(NOTIFICATION_INDICATOR_FORMATTER_STRING);
+
+ mController = new DreamOverlayStatusBarViewController(
+ mView,
+ mResources,
+ mConnectivityManager,
+ mTouchSession,
+ mAlarmManager,
+ mNextAlarmController,
+ mDateFormatUtil,
+ mSensorPrivacyController,
+ mNotificationListener,
+ mZenModeController);
+ }
+
+ @Test
+ public void testOnViewAttachedAddsCallbacks() {
+ mController.onViewAttached();
+ verify(mNextAlarmController).addCallback(any());
+ verify(mSensorPrivacyController).addCallback(any());
+ verify(mZenModeController).addCallback(any());
}
@Test
@@ -71,28 +129,97 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ public void testOnViewAttachedShowsWifiIconWhenWifiUnavailable() {
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(false);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
- verify(mView).showWifiStatus(true);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true);
}
@Test
- public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ public void testOnViewAttachedHidesWifiIconWhenWifiAvailable() {
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(true);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
}
@Test
- public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() {
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
mController.onViewAttached();
- verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsAlarmIconWhenAlarmExists() {
+ when(mAlarmClockInfo.getTriggerTime()).thenReturn(1L);
+ when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(mAlarmClockInfo);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
+ }
+
+ @Test
+ public void testOnViewAttachedHidesAlarmIconWhenNoAlarmExists() {
+ when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
+ }
+
+ @Test
+ public void testOnViewAttachedShowsMicCameraIconWhenDisabled() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesMicCameraIconWhenEnabled() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(false);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(false);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsNotificationsIconWhenNotificationsExist() {
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ }
+
+ @Test
+ public void testOnViewAttachedHidesNotificationsIconWhenNoNotificationsExist() {
+ when(mNotificationListener.getActiveNotifications()).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
+ }
+
+ @Test
+ public void testOnViewAttachedShowsPriorityModeIconWhenEnabled() {
+ when(mZenModeController.getZen()).thenReturn(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesPriorityModeIconWhenDisabled() {
+ when(mZenModeController.getZen()).thenReturn(
+ Settings.Global.ZEN_MODE_OFF);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false);
}
@Test
@@ -103,10 +230,19 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
- // Make sure wifi starts out unavailable when onViewAttached is called.
+ public void testOnViewDetachedRemovesCallbacks() {
+ mController.onViewDetached();
+ verify(mNextAlarmController).removeCallback(any());
+ verify(mSensorPrivacyController).removeCallback(any());
+ verify(mZenModeController).removeCallback(any());
+ }
+
+ @Test
+ public void testWifiIconHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called, and then returns
+ // true on the second query.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(false);
+ .thenReturn(false).thenReturn(true);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
@@ -114,14 +250,15 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
callbackCapture.getValue().onAvailable(mNetwork);
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
}
@Test
- public void testWifiStatusShownWhenWifiBecomesUnavailable() {
- // Make sure wifi starts out available when onViewAttached is called.
+ public void testWifiIconShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called, then returns false
+ // on the second query.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(true);
+ .thenReturn(true).thenReturn(false);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
@@ -129,11 +266,11 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
callbackCapture.getValue().onLost(mNetwork);
- verify(mView).showWifiStatus(true);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true);
}
@Test
- public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ public void testWifiIconHiddenWhenCapabilitiesChange() {
// Make sure wifi starts out unavailable when onViewAttached is called.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(false);
@@ -146,6 +283,102 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(true);
callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
+ }
+
+ @Test
+ public void testNotificationsIconShownWhenNotificationAdded() {
+ mController.onViewAttached();
+
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications);
+
+ final ArgumentCaptor<NotificationListener.NotificationHandler> callbackCapture =
+ ArgumentCaptor.forClass(NotificationListener.NotificationHandler.class);
+ verify(mNotificationListener).addNotificationHandler(callbackCapture.capture());
+ callbackCapture.getValue().onNotificationPosted(mStatusBarNotification, mRankingMap);
+
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ }
+
+ @Test
+ public void testNotificationsIconHiddenWhenLastNotificationRemoved() {
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications)
+ .thenReturn(null);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<NotificationListener.NotificationHandler> callbackCapture =
+ ArgumentCaptor.forClass(NotificationListener.NotificationHandler.class);
+ verify(mNotificationListener).addNotificationHandler(callbackCapture.capture());
+ callbackCapture.getValue().onNotificationPosted(mStatusBarNotification, mRankingMap);
+
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
+ }
+
+ @Test
+ public void testMicCameraIconShownWhenSensorsBlocked() {
+ mController.onViewAttached();
+
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true);
+
+ final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class);
+ verify(mSensorPrivacyController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onSensorBlockedChanged(
+ SensorPrivacyManager.Sensors.MICROPHONE, true);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true);
+ }
+
+ @Test
+ public void testMicCameraIconHiddenWhenSensorsNotBlocked() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true).thenReturn(false);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true).thenReturn(false);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class);
+ verify(mSensorPrivacyController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onSensorBlockedChanged(
+ SensorPrivacyManager.Sensors.MICROPHONE, false);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false);
+ }
+
+ @Test
+ public void testPriorityModeIconShownWhenZenModeEnabled() {
+ mController.onViewAttached();
+
+ when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+
+ final ArgumentCaptor<ZenModeController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(ZenModeController.Callback.class);
+ verify(mZenModeController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true);
+ }
+
+ @Test
+ public void testPriorityModeIconHiddenWhenZenModeDisabled() {
+ when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ .thenReturn(Settings.Global.ZEN_MODE_OFF);
+ mController.onViewAttached();
+
+
+ final ArgumentCaptor<ZenModeController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(ZenModeController.Callback.class);
+ verify(mZenModeController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index cad98f4dc713..6e01541c75e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.dreams.touch;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -27,19 +26,21 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -51,8 +52,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Random;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@@ -60,7 +59,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
- StatusBar mStatusBar;
+ CentralSurfaces mCentralSurfaces;
@Mock
NotificationShadeWindowController mNotificationShadeWindowController;
@@ -89,38 +88,51 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
VelocityTracker mVelocityTracker;
+ final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+
private static final float TOUCH_REGION = .3f;
- private static final float SCREEN_HEIGHT_PX = 100;
+ private static final int SCREEN_WIDTH_PX = 1024;
+ private static final int SCREEN_HEIGHT_PX = 100;
@Before
public void setup() {
+ mDisplayMetrics.widthPixels = SCREEN_WIDTH_PX;
+ mDisplayMetrics.heightPixels = SCREEN_HEIGHT_PX;
+
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
+ mDisplayMetrics,
mStatusBarKeyguardViewManager,
- mStatusBar,
+ mCentralSurfaces,
mNotificationShadeWindowController,
mValueAnimatorCreator,
mVelocityTrackerFactory,
mFlingAnimationUtils,
mFlingAnimationUtilsClosing,
TOUCH_REGION);
- when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX);
+
+ when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
}
- private static void beginValidSwipe(GestureDetector.OnGestureListener listener) {
- listener.onDown(MotionEvent.obtain(0, 0,
- MotionEvent.ACTION_DOWN, 0,
- SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0));
- }
-
/**
* Ensures expansion only happens when touch down happens in valid part of the screen.
*/
- @FlakyTest
@Test
public void testSessionStart() {
+ final Region region = Region.obtain();
+ mTouchHandler.getTouchInitiationRegion(region);
+
+ final Rect bounds = region.getBounds();
+
+ final Rect expected = new Rect();
+
+ expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX,
+ SCREEN_HEIGHT_PX);
+
+ assertThat(bounds).isEqualTo(expected);
+
mTouchHandler.onSessionStart(mTouchSession);
verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any());
ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
@@ -130,34 +142,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
verify(mTouchSession).registerInputListener(eventListenerCaptor.capture());
- final Random random = new Random(System.currentTimeMillis());
-
- // If an initial touch down meeting criteria has been met, scroll behavior should be
- // ignored.
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
- // A touch at the top of the screen should also not trigger listening.
- final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
- 0, 0, 0);
-
- gestureListenerCaptor.getValue().onDown(touchDownEvent);
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
// A touch within range at the bottom of the screen should trigger listening
- beginValidSwipe(gestureListenerCaptor.getValue());
assertThat(gestureListenerCaptor.getValue()
.onScroll(Mockito.mock(MotionEvent.class),
Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isTrue();
+ 1,
+ 2)).isTrue();
}
/**
@@ -170,8 +160,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float scrollAmount = .3f;
final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
@@ -203,8 +191,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
when(mVelocityTracker.getYVelocity()).thenReturn(velocityY);
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float distanceY = SCREEN_HEIGHT_PX * position;
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 74b217b2fdbf..29f56e0d8bf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -21,10 +21,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.GestureDetector;
@@ -137,6 +140,81 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
}
@Test
+ public void testEntryTouchZone() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is not delivered.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler, never()).onSessionStart(any());
+
+ // Make sure touch inside region causes session start.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+ }
+
+ @Test
+ public void testSessionCount() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ final DreamTouchHandler unzonedTouchHandler = Mockito.mock(DreamTouchHandler.class);
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler, unzonedTouchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is delivered to unzoned touch handler.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+
+ ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionCaptor = ArgumentCaptor.forClass(
+ DreamTouchHandler.TouchSession.class);
+
+ // Make sure only one active session.
+ {
+ verify(unzonedTouchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(1);
+ touchSession.pop();
+ environment.executeAll();
+ }
+
+ // Make sure touch inside the touch region.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+
+ // Make sure there are two active sessions.
+ {
+ verify(touchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(2);
+ touchSession.pop();
+ }
+ }
+
+ @Test
public void testInputEventPropagation() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 953be7d6f002..6fead9efa767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -60,7 +60,7 @@ import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -113,7 +113,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
@@ -158,7 +158,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mRingerModeTracker,
mHandler,
mPackageManager,
- Optional.of(mStatusBar),
+ Optional.of(mCentralSurfaces),
mKeyguardUpdateMonitor,
mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -227,7 +227,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(true).when(mStatusBar).isKeyguardShowing();
+ doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -242,7 +242,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- verify(mStatusBar).animateExpandSettingsPanel(null);
+ verify(mCentralSurfaces).animateExpandSettingsPanel(null);
}
@Test
@@ -251,7 +251,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mStatusBar).isKeyguardShowing();
+ doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -266,7 +266,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- verify(mStatusBar).animateExpandNotificationsPanel();
+ verify(mCentralSurfaces).animateExpandNotificationsPanel();
}
@Test
@@ -432,7 +432,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mStatusBar).isKeyguardShowing();
+ doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index 809b8901f867..adb59eca1e08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -55,6 +55,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var appIconDrawable: Drawable
@Mock
+ private lateinit var logger: MediaTttLogger
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -69,7 +71,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
- context, windowManager, viewUtil, fakeExecutor, tapGestureDetector
+ context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector
)
}
@@ -94,20 +96,22 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Test
fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
- controllerCommon.displayChip(getState())
+ val state = getState()
+ controllerCommon.displayChip(state)
reset(windowManager)
- fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
+ fakeClock.advanceTime(state.getTimeoutMs() - 1)
verify(windowManager, never()).removeView(any())
}
@Test
fun displayChip_chipDisappearsAfterTimeout() {
- controllerCommon.displayChip(getState())
+ val state = getState()
+ controllerCommon.displayChip(state)
reset(windowManager)
- fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+ fakeClock.advanceTime(state.getTimeoutMs() + 1)
verify(windowManager).removeView(any())
}
@@ -115,7 +119,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Test
fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
// First, display the chip
- controllerCommon.displayChip(getState())
+ val state = getState()
+ controllerCommon.displayChip(state)
// After some time, re-display the chip
val waitTime = 1000L
@@ -123,7 +128,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(getState())
// Wait until the timeout for the first display would've happened
- fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
+ fakeClock.advanceTime(state.getTimeoutMs() - waitTime + 1)
// Verify we didn't hide the chip
verify(windowManager, never()).removeView(any())
@@ -132,33 +137,36 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Test
fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
// First, display the chip
- controllerCommon.displayChip(getState())
+ val state = getState()
+ controllerCommon.displayChip(state)
// After some time, re-display the chip
fakeClock.advanceTime(1000L)
controllerCommon.displayChip(getState())
// Ensure we still hide the chip eventually
- fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+ fakeClock.advanceTime(state.getTimeoutMs() + 1)
verify(windowManager).removeView(any())
}
@Test
- fun removeChip_chipRemovedAndGestureDetectionStopped() {
+ fun removeChip_chipRemovedAndGestureDetectionStoppedAndRemovalLogged() {
// First, add the chip
controllerCommon.displayChip(getState())
// Then, remove it
- controllerCommon.removeChip()
+ val reason = "test reason"
+ controllerCommon.removeChip(reason)
verify(windowManager).removeView(any())
verify(tapGestureDetector).removeOnGestureDetectedCallback(any())
+ verify(logger).logChipRemoval(reason)
}
@Test
fun removeChip_noAdd_viewNotRemoved() {
- controllerCommon.removeChip()
+ controllerCommon.removeChip("reason")
verify(windowManager, never()).removeView(any())
}
@@ -222,12 +230,19 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
inner class TestControllerCommon(
context: Context,
+ logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<MediaTttChipState>(
- context, windowManager, viewUtil, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
+ context,
+ logger,
+ windowManager,
+ viewUtil,
+ mainExecutor,
+ tapGestureDetector,
+ R.layout.media_ttt_chip
) {
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
new file mode 100644
index 000000000000..d95e5c48256c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.common
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTracker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
+
+@SmallTest
+class MediaTttLoggerTest : SysuiTestCase() {
+
+ private lateinit var buffer: LogBuffer
+ private lateinit var logger: MediaTttLogger
+
+ @Before
+ fun setUp () {
+ buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ }
+
+ @Test
+ fun logStateChange_bufferHasDeviceTypeTagAndStateNameAndId() {
+ val stateName = "test state name"
+ val id = "test id"
+
+ logger.logStateChange(stateName, id)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+
+ assertThat(actualString).contains(DEVICE_TYPE_TAG)
+ assertThat(actualString).contains(stateName)
+ assertThat(actualString).contains(id)
+ }
+
+ @Test
+ fun logChipRemoval_bufferHasDeviceTypeAndReason() {
+ val reason = "test reason"
+ logger.logChipRemoval(reason)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+
+ assertThat(actualString).contains(DEVICE_TYPE_TAG)
+ assertThat(actualString).contains(reason)
+ }
+}
+
+private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 86d4c1b05ed3..56f45896436c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -31,6 +31,7 @@ import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.FakeExecutor
@@ -60,6 +61,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var applicationInfo: ApplicationInfo
@Mock
+ private lateinit var logger: MediaTttLogger
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -83,6 +86,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
controllerReceiver = MediaTttChipControllerReceiver(
commandQueue,
context,
+ logger,
windowManager,
viewUtil,
FakeExecutor(FakeSystemClock()),
@@ -142,6 +146,18 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
}
@Test
+ fun receivesNewStateFromCommandQueue_isLogged() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ verify(logger).logStateChange(any(), any())
+ }
+
+ @Test
fun displayChip_nullAppIconDrawable_iconIsFromPackageName() {
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, "appName")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 88888f0da0f7..fd1d76a5d02d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.FakeExecutor
@@ -62,6 +63,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var applicationInfo: ApplicationInfo
@Mock
+ private lateinit var logger: MediaTttLogger
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -69,6 +72,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
+ private lateinit var fakeClock: FakeSystemClock
+ private lateinit var fakeExecutor: FakeExecutor
@Before
fun setUp() {
@@ -82,12 +87,16 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
+ fakeClock = FakeSystemClock()
+ fakeExecutor = FakeExecutor(fakeClock)
+
controllerSender = MediaTttChipControllerSender(
commandQueue,
context,
+ logger,
windowManager,
viewUtil,
- FakeExecutor(FakeSystemClock()),
+ fakeExecutor,
TapGestureDetector(context)
)
@@ -223,6 +232,17 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
@Test
+ fun receivesNewStateFromCommandQueue_isLogged() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null
+ )
+
+ verify(logger).logStateChange(any(), any())
+ }
+
+ @Test
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToStartCast()
controllerSender.displayChip(state)
@@ -460,6 +480,52 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
+ @Test
+ fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() {
+ controllerSender.displayChip(transferToReceiverTriggered())
+ fakeClock.advanceTime(1000L)
+
+ controllerSender.removeChip("fakeRemovalReason")
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
+ val state = transferToReceiverTriggered()
+ controllerSender.displayChip(state)
+ fakeClock.advanceTime(1000L)
+ controllerSender.removeChip("fakeRemovalReason")
+
+ fakeClock.advanceTime(state.getTimeoutMs() + 1)
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() {
+ controllerSender.displayChip(transferToThisDeviceTriggered())
+ fakeClock.advanceTime(1000L)
+
+ controllerSender.removeChip("fakeRemovalReason")
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
+ val state = transferToThisDeviceTriggered()
+ controllerSender.displayChip(state)
+ fakeClock.advanceTime(1000L)
+ controllerSender.removeChip("fakeRemovalReason")
+
+ fakeClock.advanceTime(state.getTimeoutMs() + 1)
+
+ verify(windowManager).removeView(any())
+ }
+
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 3a95ed3da84d..634d9e4642b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -37,8 +37,7 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -91,7 +90,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 5a652fc15a48..eb1e1a2e3d04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -94,7 +94,7 @@ import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -160,7 +160,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private AssistManager mAssistManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -189,7 +189,7 @@ public class NavigationBarTest extends SysuiTestCase {
mock(AccessibilityButtonModeObserver.class),
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
- () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+ () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
mock(NavigationModeController.class), mock(UserTracker.class),
mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
@@ -282,7 +282,7 @@ public class NavigationBarTest extends SysuiTestCase {
NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
- doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView();
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(defaultNavBar).checkNavBarModes();
doNothing().when(externalNavBar).checkNavBarModes();
@@ -318,7 +318,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
- doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView();
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(mNavigationBar).checkNavBarModes();
mNavigationBar.createView(null, /* initialVisibility= */ true);
@@ -335,7 +335,7 @@ public class NavigationBarTest extends SysuiTestCase {
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mStatusBar).isKeyguardShowing();
+ doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
@@ -401,7 +401,7 @@ public class NavigationBarTest extends SysuiTestCase {
mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(Recents.class)),
- () -> Optional.of(mStatusBar),
+ () -> Optional.of(mCentralSurfaces),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 721809c83cd8..91f8a403f3b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -48,7 +48,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -89,14 +89,14 @@ public class PowerUITest extends SysuiTestCase {
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
- @Mock private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
- @Mock private StatusBar mStatusBar;
+ @Mock private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mStatusBarOptionalLazy.get()).thenReturn(Optional.of(mStatusBar));
+ when(mCentralSurfacesOptionalLazy.get()).thenReturn(Optional.of(mCentralSurfaces));
createPowerUi();
mSkinThermalEventListener = mPowerUI.new SkinThermalEventListener();
@@ -430,12 +430,6 @@ public class PowerUITest extends SysuiTestCase {
state.mIsPowerSaver = true;
shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
assertThat(shouldShow).isFalse();
-
- state.mIsPowerSaver = false;
- // if disabled we should not show the low warning.
- state.mIsLowLevelWarningEnabled = false;
- shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
- assertThat(shouldShow).isFalse();
}
@Test
@@ -685,7 +679,7 @@ public class PowerUITest extends SysuiTestCase {
private void createPowerUi() {
mPowerUI = new PowerUI(
- mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarOptionalLazy,
+ mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
mMockWarnings, mEnhancedEstimates, mPowerManager);
mPowerUI.mThermalService = mThermalServiceMock;
}
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 4ab39261e825..534c7e7f2029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -56,7 +56,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -142,7 +142,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
mock(PluginManager.class), mock(TunerService.class),
() -> mock(AutoTileManager.class), mock(DumpManager.class),
- mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
+ mock(BroadcastDispatcher.class), Optional.of(mock(CentralSurfaces.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class));
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 8872e28647a7..e67d37ac3c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -65,7 +65,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.FakeSettings;
@@ -112,7 +112,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Mock
private QSTile.State mMockState;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private QSLogger mQSLogger;
@Mock
@@ -152,7 +152,7 @@ public class QSTileHostTest extends SysuiTestCase {
QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
- mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
+ mBroadcastDispatcher, mCentralSurfaces, mQSLogger, mUiEventLogger, mUserTracker,
mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
mTileLifecycleManagerFactory);
setUpTileFactory();
@@ -437,15 +437,15 @@ public class QSTileHostTest extends SysuiTestCase {
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
- BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
- UiEventLogger uiEventLogger, UserTracker userTracker,
+ BroadcastDispatcher broadcastDispatcher, CentralSurfaces centralSurfaces,
+ QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
TileLifecycleManager.Factory tileLifecycleManagerFactory) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
- Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister, tileServiceRequestControllerBuilder,
+ Optional.of(centralSurfaces), qsLogger, uiEventLogger, userTracker,
+ secureSettings, customTileStatePersister, tileServiceRequestControllerBuilder,
tileLifecycleManagerFactory);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index b559d18d9520..04b50d8d98c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -34,6 +35,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
@@ -56,7 +58,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -64,10 +66,10 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
private static final int TEST_FAIL_TIMEOUT = 5000;
private final PackageManagerAdapter mMockPackageManagerAdapter =
- Mockito.mock(PackageManagerAdapter.class);
+ mock(PackageManagerAdapter.class);
private final BroadcastDispatcher mMockBroadcastDispatcher =
- Mockito.mock(BroadcastDispatcher.class);
- private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
+ mock(BroadcastDispatcher.class);
+ private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
private ComponentName mTileServiceComponentName;
private Intent mTileServiceIntent;
private UserHandle mUser;
@@ -95,7 +97,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
- Mockito.mock(IQSService.class),
+ mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
@@ -269,6 +271,25 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
assertTrue(mStateManager.isToggleableTile());
}
+ @Test
+ public void testFalseBindCallsUnbind() {
+ Context falseContext = mock(Context.class);
+ when(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false);
+ TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext,
+ mock(IQSService.class),
+ mMockPackageManagerAdapter,
+ mMockBroadcastDispatcher,
+ mTileServiceIntent,
+ mUser);
+
+ manager.setBindService(true);
+
+ ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+ verify(falseContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
+
+ verify(falseContext).unbindService(captor.getValue());
+ }
+
private static class TestContextWrapper extends ContextWrapper {
private IntentFilter mLastIntentFilter;
private int mLastFlag;
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 e39d6a1bfc01..19a9863f6f5e 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
@@ -50,7 +50,7 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -91,7 +91,7 @@ public class TileServicesTest extends SysuiTestCase {
@Mock
private DumpManager mDumpManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private QSLogger mQSLogger;
@Mock
@@ -132,7 +132,7 @@ public class TileServicesTest extends SysuiTestCase {
() -> mAutoTileManager,
mDumpManager,
mock(BroadcastDispatcher.class),
- Optional.of(mStatusBar),
+ Optional.of(mCentralSurfaces),
mQSLogger,
mUiEventLogger,
mUserTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index bfe875ccd60d..7ab49584f0e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -19,7 +19,7 @@ package com.android.systemui.screenshot;
import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -41,7 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -59,7 +59,7 @@ import java.util.concurrent.TimeoutException;
public class ActionProxyReceiverTest extends SysuiTestCase {
@Mock
- private StatusBar mMockStatusBar;
+ private CentralSurfaces mMockCentralSurfaces;
@Mock
private ActivityManagerWrapper mMockActivityManagerWrapper;
@Mock
@@ -83,7 +83,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+ verify(mMockCentralSurfaces, never()).executeRunnableDismissingKeyguard(
any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
verify(mMockPendingIntent).send(
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
@@ -96,13 +96,13 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
doAnswer((Answer<Object>) invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
- }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+ }).when(mMockCentralSurfaces).executeRunnableDismissingKeyguard(
any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+ verify(mMockCentralSurfaces).executeRunnableDismissingKeyguard(
any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
verify(mMockPendingIntent).send(
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
@@ -135,7 +135,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
if (withStatusBar) {
return new ActionProxyReceiver(
- Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+ Optional.of(mMockCentralSurfaces), mMockActivityManagerWrapper,
mMockScreenshotSmartActions);
} else {
return new ActionProxyReceiver(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 6c29ecc7ae50..11f76a381ad4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -448,9 +448,10 @@ public class CommandQueueTest extends SysuiTestCase {
@Test
public void testOnBiometricAuthenticated() {
- mCommandQueue.onBiometricAuthenticated();
+ final int id = 12;
+ mCommandQueue.onBiometricAuthenticated(id);
waitForIdleSync();
- verify(mCallbacks).onBiometricAuthenticated();
+ verify(mCallbacks).onBiometricAuthenticated(eq(id));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 75ccd8f03a01..25a5b900f15a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.After
import org.junit.Assert.assertFalse
@@ -72,7 +72,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var depthController: NotificationShadeDepthController
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@Mock lateinit var expandHelperCallback: ExpandHelper.Callback
- @Mock lateinit var statusbar: StatusBar
+ @Mock lateinit var mCentralSurfaces: CentralSurfaces
@Mock lateinit var qS: QS
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -106,7 +106,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
transitionController.notificationPanelController = notificationPanelController
- transitionController.statusbar = statusbar
+ transitionController.centralSurfaces = mCentralSurfaces
transitionController.qS = qS
transitionController.setStackScroller(nsslController)
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
@@ -118,7 +118,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
- clearInvocations(statusbar)
+ clearInvocations(mCentralSurfaces)
}
@After
@@ -175,7 +175,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Test
fun testDontGoWhenShadeDisabled() {
- whenever(statusbar.isShadeDisabled).thenReturn(true)
+ whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
@@ -194,7 +194,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
@@ -203,7 +203,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 4213b070d0c2..a4ce9cd12f73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -47,8 +47,8 @@ import org.mockito.MockitoAnnotations;
/**
* Verifies that particular sets of dependencies don't have dependencies on others. For example,
- * code managing notifications shouldn't directly depend on StatusBar, since there are platforms
- * which want to manage notifications, but don't use StatusBar.
+ * code managing notifications shouldn't directly depend on CentralSurfaces, since there are
+ * platforms which want to manage notifications, but don't use CentralSurfaces.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index bd9f91f45e25..2691ff98f4ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -49,7 +49,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.google.android.collect.Sets;
@@ -105,7 +105,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
mVisibilityProvider,
mEntryManager,
mock(RemoteInputNotificationRebuilder.class),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
@@ -196,7 +196,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -211,7 +211,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
visibilityProvider,
notificationEntryManager,
rebuilder,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 7fafb24317b7..407044b5378a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -54,7 +54,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -121,7 +120,6 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mock(KeyguardBypassController.class),
Optional.of(mock(Bubbles.class)),
mock(DynamicPrivacyController.class),
- mock(ForegroundServiceSectionController.class),
mock(DynamicChildBindController.class),
mock(LowPriorityInflationHelper.class),
mock(AssistantFeedbackController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index e0689f3da081..3500e4d5f701 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -45,7 +45,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import org.junit.Before;
@@ -103,7 +103,7 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mVisibilityProvider,
mNotificationEntryManager,
new RemoteInputNotificationRebuilder(mContext),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f2b7bf515c45..0fff5f5b47e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -201,7 +201,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 52189e417017..7fc5ece670d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -61,7 +61,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -191,7 +190,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 7d8e0d26464c..e9d8f5830131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -82,8 +82,8 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
@@ -124,7 +124,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
@Mock private OnSettingsClickListener mOnSettingsClickListener;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private AccessibilityManager mAccessibilityManager;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private INotificationManager mINotificationManager;
@@ -155,7 +155,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext,
- () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager,
+ () -> Optional.of(mCentralSurfaces), mHandler, mHandler, mAccessibilityManager,
mHighPriorityProvider, mINotificationManager, mNotificationEntryManager,
mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index c4f954fee1f6..1561b5a5d22e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -58,7 +58,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -68,14 +67,13 @@ import com.android.systemui.statusbar.notification.collection.legacy.VisualStabi
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -118,7 +116,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private ScrimController mScrimController;
@Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
@Mock private SectionHeaderController mSilentHeaderController;
@@ -129,9 +127,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private IStatusBarService mIStatusBarService;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
- @Mock private ForegroundServiceSectionController mFgServicesSectionController;
- @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
@Mock private LayoutInflater mLayoutInflater;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private VisualStabilityManager mVisualStabilityManager;
@@ -151,8 +146,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
- when(mFgServicesSectionController.createView(mLayoutInflater))
- .thenReturn(mForegroundServiceDungeonView);
mController = new NotificationStackScrollLayoutController(
true,
@@ -175,7 +168,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
new FalsingManagerFake(),
mResources,
mNotificationSwipeHelperBuilder,
- mStatusBar,
+ mCentralSurfaces,
mScrimController,
mLegacyGroupManager,
mLegacyGroupManager,
@@ -187,8 +180,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mLockscreenShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
- mFgFeatureController,
- mFgServicesSectionController,
mLayoutInflater,
mRemoteInputManager,
mVisualStabilityManager,
@@ -399,22 +390,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
- public void testForegroundDismissEnabled() {
- when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
- mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
- mForegroundServiceDungeonView);
- }
-
- @Test
- public void testForegroundDismissaDisabled() {
- when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
- mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
- any(ForegroundServiceDungeonView.class));
- }
-
- @Test
public void testUpdateFooter_remoteInput() {
ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
ArgumentCaptor.forClass(RemoteInputController.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4f731ed5f72c..eafcc354fac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -61,9 +61,9 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import org.junit.Assert;
@@ -89,7 +89,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
private AmbientState mAmbientState;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private StatusBar mBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private SysuiStatusBarStateController mBarState;
@Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
@Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@@ -141,7 +141,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController);
- mStackScroller.setStatusBar(mBar);
+ mStackScroller.setCentralSurfaces(mCentralSurfaces);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNoticationRoundessManager())
@@ -149,7 +149,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setController(mStackScrollLayoutController);
// Stub out functionality that isn't necessary to test.
- doNothing().when(mBar)
+ doNothing().when(mCentralSurfaces)
.executeRunnableDismissingKeyguard(any(Runnable.class),
any(Runnable.class),
anyBoolean(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 25950f2e8caa..9bfb2c4ce00c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -57,8 +57,8 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
- @Mock private StatusBar mStatusBar;
+public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private ShadeController mShadeController;
@Mock private CommandQueue mCommandQueue;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@@ -81,14 +81,14 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private LightBarController mLightBarController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
- StatusBarCommandQueueCallbacks mSbcqCallbacks;
+ CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSbcqCallbacks = new StatusBarCommandQueueCallbacks(
- mStatusBar,
+ mSbcqCallbacks = new CentralSurfacesCommandQueueCallbacks(
+ mCentralSurfaces,
mContext,
mContext.getResources(),
mShadeController,
@@ -122,13 +122,13 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
@Test
public void testDisableNotificationShade() {
- when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
when(mCommandQueue.panelsEnabled()).thenReturn(false);
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mCentralSurfaces).updateQsExpansionEnabled();
verify(mShadeController).animateCollapsePanels();
// Trying to open it does nothing.
@@ -140,12 +140,13 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
@Test
public void testEnableNotificationShade() {
- when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+ when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled2())
+ .thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
when(mCommandQueue.panelsEnabled()).thenReturn(true);
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
- verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mCentralSurfaces).updateQsExpansionEnabled();
verify(mShadeController, never()).animateCollapsePanels();
// Can now be opened.
@@ -158,13 +159,13 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
@Test
public void testSuppressAmbientDisplay_suppress() {
mSbcqCallbacks.suppressAmbientDisplay(true);
- verify(mDozeServiceHost).setDozeSuppressed(true);
+ verify(mDozeServiceHost).setAlwaysOnSuppressed(true);
}
@Test
public void testSuppressAmbientDisplay_unsuppress() {
mSbcqCallbacks.suppressAmbientDisplay(false);
- verify(mDozeServiceHost).setDozeSuppressed(false);
+ verify(mDozeServiceHost).setAlwaysOnSuppressed(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
index 09794397c31e..953a330d2e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
@@ -139,7 +139,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -177,12 +177,12 @@ import dagger.Lazy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class StatusBarTest extends SysuiTestCase {
+public class CentralSurfacesTest extends SysuiTestCase {
private static final int FOLD_STATE_FOLDED = 0;
private static final int FOLD_STATE_UNFOLDED = 1;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
@@ -252,14 +252,12 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
- @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
- @Mock private StatusBarComponent mStatusBarComponent;
+ @Mock private CentralSurfacesComponent.Factory mStatusBarComponentFactory;
+ @Mock private CentralSurfacesComponent mCentralSurfacesComponent;
@Mock private PluginManager mPluginManager;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock private ScreenPinningRequest mScreenPinningRequest;
- @Mock private StatusBarNotificationActivityStarter.Builder
- mStatusBarNotificationActivityStarterBuilder;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@@ -336,8 +334,6 @@ public class StatusBarTest extends SysuiTestCase {
mContext.setTheme(R.style.Theme_SystemUI_LightWallpaper);
when(mStackScrollerController.getView()).thenReturn(mStackScroller);
- when(mStackScrollerController.getNotificationListContainer()).thenReturn(
- mNotificationListContainer);
when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
@@ -368,8 +364,8 @@ public class StatusBarTest extends SysuiTestCase {
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
- when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
- when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
+ when(mStatusBarComponentFactory.create()).thenReturn(mCentralSurfacesComponent);
+ when(mCentralSurfacesComponent.getNotificationShadeWindowViewController()).thenReturn(
mNotificationShadeWindowViewController);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
@@ -379,12 +375,12 @@ public class StatusBarTest extends SysuiTestCase {
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mStatusBar), () -> mAssistManager);
+ () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
- mStatusBar = new StatusBar(
+ mCentralSurfaces = new CentralSurfaces(
mContext,
mNotificationsController,
mock(FragmentService.class),
@@ -445,7 +441,6 @@ public class StatusBarTest extends SysuiTestCase {
mCommandQueue,
mStatusBarComponentFactory,
mPluginManager,
- mStatusBarNotificationActivityStarterBuilder,
mShadeController,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
@@ -480,8 +475,8 @@ public class StatusBarTest extends SysuiTestCase {
mDeviceStateManager,
mDreamOverlayStateController,
mWiredChargingRippleController);
- when(mKeyguardViewMediator.registerStatusBar(
- any(StatusBar.class),
+ when(mKeyguardViewMediator.registerCentralSurfaces(
+ any(CentralSurfaces.class),
any(NotificationPanelViewController.class),
any(PanelExpansionStateManager.class),
any(BiometricUnlockController.class),
@@ -492,23 +487,23 @@ public class StatusBarTest extends SysuiTestCase {
when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
mKeyguardVieMediatorCallback);
- // TODO: we should be able to call mStatusBar.start() and have all the below values
+ // TODO: we should be able to call mCentralSurfaces.start() and have all the below values
// initialized automatically.
- mStatusBar.mNotificationShadeWindowView = mNotificationShadeWindowView;
- mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
- mStatusBar.mDozeScrimController = mDozeScrimController;
- mStatusBar.mPresenter = mNotificationPresenter;
- mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
- mStatusBar.mBarService = mBarService;
- mStatusBar.mStackScroller = mStackScroller;
- mStatusBar.startKeyguard();
+ mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
+ mCentralSurfaces.mNotificationPanelViewController = mNotificationPanelViewController;
+ mCentralSurfaces.mDozeScrimController = mDozeScrimController;
+ mCentralSurfaces.mPresenter = mNotificationPresenter;
+ mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
+ mCentralSurfaces.mBarService = mBarService;
+ mCentralSurfaces.mStackScroller = mStackScroller;
+ mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
notificationLogger.setUpWithContainer(mNotificationListContainer);
}
@Test
public void testSetBouncerShowing_noCrash() {
- mStatusBar.setBouncerShowing(true);
+ mCentralSurfaces.setBouncerShowing(true);
}
@Test
@@ -516,7 +511,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -524,7 +519,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -532,7 +527,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -544,7 +539,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing hidden insecure lockscreen log",
mMetricsLogger.getLogs(),
@@ -563,7 +558,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing hidden secure lockscreen log",
mMetricsLogger.getLogs(),
@@ -582,7 +577,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing insecure lockscreen showing",
mMetricsLogger.getLogs(),
@@ -601,7 +596,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing secure lockscreen showing log",
mMetricsLogger.getLogs(),
@@ -620,7 +615,7 @@ public class StatusBarTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing bouncer log",
mMetricsLogger.getLogs(),
@@ -721,7 +716,7 @@ public class StatusBarTest extends SysuiTestCase {
@Test
public void testLogHidden() {
try {
- mStatusBar.handleVisibleToUserChanged(false);
+ mCentralSurfaces.handleVisibleToUserChanged(false);
mUiBgExecutor.runAllReady();
verify(mBarService, times(1)).onPanelHidden();
verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
@@ -736,10 +731,10 @@ public class StatusBarTest extends SysuiTestCase {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 1);
@@ -755,10 +750,10 @@ public class StatusBarTest extends SysuiTestCase {
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(true, 5);
@@ -773,10 +768,10 @@ public class StatusBarTest extends SysuiTestCase {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 5);
@@ -788,18 +783,18 @@ public class StatusBarTest extends SysuiTestCase {
@Test
public void testDump_DoesNotCrash() {
- mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
+ mCentralSurfaces.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
}
@Test
public void testDumpBarTransitions_DoesNotCrash() {
- StatusBar.dumpBarTransitions(
+ CentralSurfaces.dumpBarTransitions(
new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null);
}
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.notifyBiometricAuthModeChanged();
+ mCentralSurfaces.notifyBiometricAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@@ -808,81 +803,81 @@ public class StatusBarTest extends SysuiTestCase {
// Simulate unlocking from AoD with fingerprint.
when(mBiometricUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@Test
public void testTransitionLaunch_goesToUnlocked() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@Test
public void testSetExpansionAffectsAlpha_whenKeyguardShowingButGoingAwayForAnyReason() {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
}
@Test
public void testTransitionLaunch_noPreview_doesntGoUnlocked() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@Test
public void testSetOccluded_propagatesToScrimController() {
- mStatusBar.setOccluded(true);
+ mCentralSurfaces.setOccluded(true);
verify(mScrimController).setKeyguardOccluded(eq(true));
reset(mScrimController);
- mStatusBar.setOccluded(false);
+ mCentralSurfaces.setOccluded(false);
verify(mScrimController).setKeyguardOccluded(eq(false));
}
@Test
public void testPulseWhileDozing_updatesScrimController() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mDozeServiceHost.isPulsing()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
// Ending a pulse should take it back to keyguard state
when(mDozeServiceHost.isPulsing()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@@ -890,45 +885,45 @@ public class StatusBarTest extends SysuiTestCase {
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// By default, showKeyguardImpl sets state to KEYGUARD.
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.showKeyguardImpl();
verify(mStatusBarStateController).setState(
eq(StatusBarState.KEYGUARD), eq(false) /* force */);
}
@Test
public void testOnStartedWakingUp_isNotDozing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
// TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
verify(mNotificationPanelViewController, times(2)).expand(eq(false));
clearInvocations(mNotificationPanelViewController);
- mStatusBar.mWakefulnessObserver.onStartedWakingUp();
+ mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp();
verify(mDozeServiceHost).stopDozing();
verify(mNotificationPanelViewController).expand(eq(false));
}
@Test
public void testOnStartedWakingUp_doesNotDismissBouncer_whenPulsing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
clearInvocations(mNotificationPanelViewController);
- mStatusBar.setBouncerShowing(true);
- mStatusBar.mWakefulnessObserver.onStartedWakingUp();
+ mCentralSurfaces.setBouncerShowing(true);
+ mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp();
verify(mNotificationPanelViewController, never()).expand(anyBoolean());
}
@Test
public void testRegisterBroadcastsonDispatcher() {
- mStatusBar.registerBroadcastReceiver();
+ mCentralSurfaces.registerBroadcastReceiver();
verify(mBroadcastDispatcher).registerReceiver(
any(BroadcastReceiver.class),
any(IntentFilter.class),
@@ -938,7 +933,7 @@ public class StatusBarTest extends SysuiTestCase {
@Test
public void testUpdateResources_updatesBouncer() {
- mStatusBar.updateResources();
+ mCentralSurfaces.updateResources();
verify(mStatusBarKeyguardViewManager).updateResources();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 6ce3b4b7b6f9..26ac70c70e7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -82,7 +82,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private PowerManager mPowerManager;
@Mock private WakefulnessLifecycle mWakefullnessLifecycle;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -103,7 +103,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
mAuthController, mNotificationIconAreaController);
mDozeServiceHost.initialize(
- mStatusBar,
+ mCentralSurfaces,
mStatusBarKeyguardViewManager,
mNotificationShadeWindowViewController,
mNotificationPanel,
@@ -119,7 +119,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
mDozeServiceHost.startDozing();
verify(mStatusBarStateController).setIsDozing(eq(true));
- verify(mStatusBar).updateIsKeyguard();
+ verify(mCentralSurfaces).updateIsKeyguard();
mDozeServiceHost.stopDozing();
verify(mStatusBarStateController).setIsDozing(eq(false));
@@ -128,8 +128,8 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Test
public void testPulseWhileDozing_updatesScrimController() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Keep track of callback to be able to stop the pulse
// DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
@@ -154,12 +154,12 @@ public class DozeServiceHostTest extends SysuiTestCase {
verify(mDozeScrimController).pulse(
pulseCallbackArgumentCaptor.capture(), eq(DozeLog.PULSE_REASON_NOTIFICATION));
- verify(mStatusBar).updateScrimController();
- reset(mStatusBar);
+ verify(mCentralSurfaces).updateScrimController();
+ reset(mCentralSurfaces);
pulseCallbackArgumentCaptor.getValue().onPulseFinished();
assertFalse(mDozeScrimController.isPulsing());
- verify(mStatusBar).updateScrimController();
+ verify(mCentralSurfaces).updateScrimController();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 421d8f6a1889..db5741c90ebc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -63,7 +63,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
@Mock private NotificationGroupManagerLegacy mGroupManager;
@Mock private View mNotificationShadeWindowView;
@Mock private VisualStabilityProvider mVSProvider;
- @Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 3257a84f14d6..31465f45af42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -25,7 +25,7 @@ import java.util.concurrent.Executor
class KeyguardBottomAreaTest : SysuiTestCase() {
@Mock
- private lateinit var mStatusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView
@Before
@@ -42,7 +42,7 @@ class KeyguardBottomAreaTest : SysuiTestCase() {
mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
- mKeyguardBottomArea.setStatusBar(mStatusBar)
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 9245c2ca9b67..1af7035a441b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -130,6 +130,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -168,7 +169,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
@@ -358,6 +359,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock
private SysUiState mSysUiState;
+ @Mock
+ private NotificationListContainer mNotificationListContainer;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -387,8 +390,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade))
.thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
- when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
- when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal))
+ .thenReturn(10);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
@@ -542,9 +545,10 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
- mKeyguardUnlockAnimationController);
+ mKeyguardUnlockAnimationController,
+ mNotificationListContainer);
mNotificationPanelViewController.initDependencies(
- mStatusBar,
+ mCentralSurfaces,
() -> {},
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
@@ -786,25 +790,31 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void testSinglePaneShadeLayout_childrenHaveConstantWidth() {
- enableSplitShade(/* enabled= */ false);
+ public void testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
+ enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth)
- .isEqualTo(mResources.getDimensionPixelSize(R.dimen.qs_panel_width));
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth)
- .isEqualTo(mResources.getDimensionPixelSize(R.dimen.notification_panel_width));
+ assertThat(getConstraintSetLayout(R.id.qs_frame).startMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
+ .isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).endMargin)
+ .isEqualTo(10);
}
@Test
- public void testSplitShadeLayout_childrenHaveZeroWidth() {
- enableSplitShade(/* enabled= */ true);
+ public void testSinglePaneLayout_childrenHaveEqualMargins() {
+ enableSplitShade(/* enabled= */ false);
mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0);
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).startMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
+ .isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).endMargin)
+ .isEqualTo(10);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
index 34a5d4b03dbb..093f92646115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
@@ -61,7 +61,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var mStatusBarStateController: SysuiStatusBarStateController
@Mock
- private lateinit var mStatusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
@Mock
private lateinit var mDockManager: DockManager
@Mock
@@ -107,10 +107,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController,
- Optional.of(mLowLightClockController)
+ Optional.of(mLowLightClockController),
+ mCentralSurfaces,
+ mNotificationShadeWindowController
)
mController.setupExpandedStatusBar()
- mController.setService(mStatusBar, mNotificationShadeWindowController)
mInteractionEventHandlerCaptor =
ArgumentCaptor.forClass(InteractionEventHandler::class.java)
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 6c33113ae58b..62a1bcdc0184 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
@@ -71,7 +71,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private DragDownHelper mDragDownHelper;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ShadeController mShadeController;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private DockManager mDockManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@@ -115,9 +115,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController,
- Optional.of(mLowLightClockController));
+ Optional.of(mLowLightClockController),
+ mCentralSurfaces,
+ mNotificationShadeWindowController);
mController.setupExpandedStatusBar();
- mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 8b93de571cc9..f4f55ccefd09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -75,7 +75,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private ViewGroup mContainer;
@Mock
@@ -118,7 +118,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
any(ViewGroup.class),
any(KeyguardBouncer.BouncerExpansionCallback.class)))
.thenReturn(mBouncer);
- when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
+ when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
@@ -138,8 +138,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
mLatencyTracker);
- mStatusBarKeyguardViewManager.registerStatusBar(
- mStatusBar,
+ mStatusBarKeyguardViewManager.registerCentralSurfaces(
+ mCentralSurfaces,
mNotificationPanelView,
new PanelExpansionStateManager(),
mBiometricUnlockController,
@@ -261,7 +261,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
- when(mStatusBar.isInLaunchTransition()).thenReturn(true);
+ when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
@@ -272,12 +272,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mStatusBar).animateKeyguardUnoccluding();
+ verify(mCentralSurfaces).animateKeyguardUnoccluding();
when(mBouncer.isShowing()).thenReturn(true);
- clearInvocations(mStatusBar);
+ clearInvocations(mCentralSurfaces);
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mStatusBar, never()).animateKeyguardUnoccluding();
+ verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
}
@Test
@@ -303,7 +303,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
- when(mStatusBar.isInLaunchTransition()).thenReturn(true);
+ when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
@@ -312,7 +312,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void setOccluded_isLaunchingActivityOverLockscreen_onKeyguardOccludedChangedCalled() {
- when(mStatusBar.isLaunchingActivityOverLockscreen()).thenReturn(true);
+ when(mCentralSurfaces.isLaunchingActivityOverLockscreen()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 743311f99ca2..ace7415f2c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -113,7 +113,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private NotificationRemoteInputManager mRemoteInputManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
@@ -203,7 +203,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mJankMonitor);
mNotificationActivityStarter =
- new StatusBarNotificationActivityStarter.Builder(
+ new StatusBarNotificationActivityStarter(
getContext(),
mock(CommandQueue.class),
mHandler,
@@ -229,18 +229,16 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
-
mNotifPipelineFlags,
mock(MetricsLogger.class),
mock(StatusBarNotificationActivityStarterLogger.class),
- mOnUserInteractionCallback)
- .setStatusBar(mStatusBar)
- .setNotificationPresenter(mock(NotificationPresenter.class))
- .setNotificationPanelViewController(
- mock(NotificationPanelViewController.class))
- .setActivityLaunchAnimator(mActivityLaunchAnimator)
- .setNotificationAnimatorControllerProvider(notificationAnimationProvider)
- .build();
+ mOnUserInteractionCallback,
+ mCentralSurfaces,
+ mock(NotificationPresenter.class),
+ mock(NotificationPanelViewController.class),
+ mActivityLaunchAnimator,
+ notificationAnimationProvider
+ );
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
@@ -269,7 +267,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
@@ -328,7 +326,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Given
sbn.getNotification().contentIntent = null;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -358,7 +356,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Given
sbn.getNotification().contentIntent = mContentIntent;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -402,6 +400,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.handleFullScreenIntent(entry);
// THEN display should try wake up for the full screen intent
- verify(mStatusBar).wakeUpForFullScreenIntent();
+ verify(mCentralSurfaces).wakeUpForFullScreenIntent();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 7d9e6b4c1620..1a3dd3a7a2a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -37,6 +37,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -79,7 +80,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
- private StatusBar mStatusBar = mock(StatusBar.class);
+ private CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private InitController mInitController = new InitController();
@Before
@@ -101,19 +102,24 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationStackScrollLayoutController.class);
when(stackScrollLayoutController.getView()).thenReturn(
mock(NotificationStackScrollLayout.class));
- when(stackScrollLayoutController.getNotificationListContainer()).thenReturn(
- mock(NotificationListContainer.class));
when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
- mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
- mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
- notificationShadeWindowView, stackScrollLayoutController,
- mock(DozeScrimController.class), mock(ScrimController.class),
- mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
+ mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
+ mContext,
+ mock(NotificationPanelViewController.class),
+ mock(HeadsUpManagerPhone.class),
+ notificationShadeWindowView,
+ mock(ActivityStarter.class),
+ stackScrollLayoutController,
+ mock(DozeScrimController.class),
+ mock(ScrimController.class),
+ mock(NotificationShadeWindowController.class),
+ mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class),
- mStatusBar,
- mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
+ mCentralSurfaces,
+ mock(ShadeControllerImpl.class),
+ mock(LockscreenShadeTransitionController.class),
mCommandQueue,
mock(NotificationViewHierarchyManager.class),
mock(NotificationLockscreenUserManager.class),
@@ -128,7 +134,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mNotificationInterruptStateProvider,
mock(NotificationRemoteInputManager.class),
mock(ConfigurationController.class),
- mock(NotifPipelineFlags.class));
+ mock(NotifPipelineFlags.class),
+ mock(NotificationRemoteInputManager.Callback.class),
+ mock(NotificationListContainer.class));
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
@@ -195,9 +203,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
.setTag("a")
.setNotification(n)
.build();
- when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true);
+ when(mCentralSurfaces.areNotificationAlertsDisabled()).thenReturn(true);
- assertTrue("StatusBar alerts disabled shouldn't allow interruptions",
+ assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions",
mInterruptSuppressor.suppressInterruptions(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 71b32c0bd106..050563a5707c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -61,7 +61,7 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
@Mock
private lateinit var globalSettings: GlobalSettings
@Mock
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
@Mock
private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock
@@ -93,8 +93,8 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
powerManager,
handler = handler
)
- controller.initialize(statusBar, lightRevealScrim)
- `when`(statusBar.notificationPanelViewController).thenReturn(
+ controller.initialize(mCentralSurfaces, lightRevealScrim)
+ `when`(mCentralSurfaces.notificationPanelViewController).thenReturn(
notificationPanelViewController)
// Screen off does not run if the panel is expanded, so we should say it's collapsed to test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 586161716829..509509401d13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -17,6 +17,8 @@ package com.android.systemui.statusbar.phone.fragment;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -27,6 +29,7 @@ import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.Context;
import android.os.Bundle;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -56,6 +59,9 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -82,6 +88,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private final CommandQueue mCommandQueue = mock(CommandQueue.class);
private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private OperatorNameViewController mOperatorNameViewController;
+ private SecureSettings mSecureSettings;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
@@ -298,6 +306,40 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
}
+ @Test
+ public void testBlockedIcons_obeysSettingForVibrateIcon_settingOff() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
+
+ // GIVEN the setting is off
+ when(mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ .thenReturn(0);
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ fragment.updateBlockedIcons();
+
+ // THEN status_bar_volume SHOULD be present in the list
+ boolean contains = fragment.getBlockedIcons().contains(str);
+ assertTrue(contains);
+ }
+
+ @Test
+ public void testBlockedIcons_obeysSettingForVibrateIcon_settingOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
+
+ // GIVEN the setting is ON
+ when(mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ .thenReturn(1);
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ fragment.updateBlockedIcons();
+
+ // THEN status_bar_volume SHOULD NOT be present in the list
+ boolean contains = fragment.getBlockedIcons().contains(str);
+ assertFalse(contains);
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this);
@@ -313,6 +355,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
+ mSecureSettings = mock(SecureSettings.class);
setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment(
@@ -334,7 +377,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
new DisableFlagsLogger()
),
- mOperatorNameViewControllerFactory);
+ mOperatorNameViewControllerFactory,
+ mSecureSettings,
+ mExecutor);
}
private void setUpDaggerComponent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index ada0453acd86..1c48eca797a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -205,17 +205,15 @@ class OngoingCallControllerTest : SysuiTestCase() {
/** Regression test for b/194731244. */
@Test
- fun onEntryUpdated_calledManyTimes_uidObserverUnregisteredManyTimes() {
- val numCalls = 4
-
- for (i in 0 until numCalls) {
+ fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() {
+ for (i in 0 until 4) {
// Re-create the notification each time so that it's considered a different object and
- // observers will get re-registered (and hopefully unregistered).
+ // will re-trigger the whole flow.
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
}
- // There should be 1 observer still registered, so we should unregister n-1 times.
- verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any())
+ verify(mockIActivityManager, times(1))
+ .registerUidObserver(any(), any(), any(), any())
}
/** Regression test for b/216248574. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 9f9cb409009f..12cfb32ab57e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -14,9 +14,12 @@
package com.android.systemui.statusbar.policy;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -24,6 +27,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Handler;
@@ -67,6 +71,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
private DeviceConfigProxy mDeviceConfigProxy;
private UiEventLoggerFake mUiEventLogger;
+ @Mock private PackageManager mPackageManager;
@Mock private AppOpsController mAppOpsController;
@Mock private UserTracker mUserTracker;
@Mock private SecureSettings mSecureSettings;
@@ -82,6 +87,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper = TestableLooper.get(this);
+
mLocationController = new LocationControllerImpl(mContext,
mAppOpsController,
mDeviceConfigProxy,
@@ -90,7 +96,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class),
mUserTracker,
- mContext.getPackageManager(),
+ mPackageManager,
mUiEventLogger,
mSecureSettings);
@@ -178,6 +184,8 @@ public class LocationControllerImplTest extends SysuiTestCase {
@Test
public void testCallbackNotified_additionalOps() {
+ // Return -1 for non system apps
+ when(mPackageManager.getPermissionFlags(any(), any(), any())).thenReturn(-1);
LocationChangeCallback callback = mock(LocationChangeCallback.class);
mLocationController.addCallback(callback);
mDeviceConfigProxy.setProperty(
@@ -190,10 +198,10 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps())
.thenReturn(ImmutableList.of(
new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.googlequicksearchbox",
+ "com.third.party.app",
System.currentTimeMillis())));
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.googlequicksearchbox", true);
+ "ccom.third.party.app", true);
mTestableLooper.processAllMessages();
@@ -201,14 +209,14 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.googlequicksearchbox", false);
+ "com.third.party.app", false);
mTestableLooper.processAllMessages();
verify(callback, times(1)).onLocationActiveChanged(false);
when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
- "com.google.android.googlequicksearchbox", true);
+ "com.third.party.app", true);
mTestableLooper.processAllMessages();
verify(callback, times(1)).onLocationActiveChanged(true);
}
@@ -227,10 +235,10 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps())
.thenReturn(ImmutableList.of(
new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms",
+ "com.system.app",
System.currentTimeMillis())));
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", true);
+ "com.system.app", true);
mTestableLooper.processAllMessages();
@@ -258,10 +266,10 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps())
.thenReturn(ImmutableList.of(
- new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.google.android.gms",
+ new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.system.app",
System.currentTimeMillis())));
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", true);
+ "com.system.app", true);
mTestableLooper.processAllMessages();
@@ -269,7 +277,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", false);
+ "com.system.app", false);
mTestableLooper.processAllMessages();
verify(callback, times(1)).onLocationActiveChanged(false);
@@ -277,7 +285,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mSecureSettings.getInt(Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0))
.thenReturn(0);
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", true);
+ "com.system.app", true);
mTestableLooper.processAllMessages();
@@ -287,6 +295,11 @@ public class LocationControllerImplTest extends SysuiTestCase {
@Test
public void testCallbackNotified_verifyMetrics() {
+ // Return -1 for non system apps and 0 for system apps.
+ when(mPackageManager.getPermissionFlags(any(),
+ eq("com.system.app"), any())).thenReturn(0);
+ when(mPackageManager.getPermissionFlags(any(),
+ eq("com.third.party.app"), any())).thenReturn(-1);
LocationChangeCallback callback = mock(LocationChangeCallback.class);
mLocationController.addCallback(callback);
mDeviceConfigProxy.setProperty(
@@ -298,16 +311,16 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps())
.thenReturn(ImmutableList.of(
- new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.google.android.gms",
+ new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.system.app",
System.currentTimeMillis()),
new AppOpItem(AppOpsManager.OP_COARSE_LOCATION, 0,
- "com.google.android.googlequicksearchbox",
+ "com.third.party.app",
System.currentTimeMillis()),
new AppOpItem(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
- "com.google.android.apps.maps",
+ "com.another.developer.app",
System.currentTimeMillis())));
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", true);
+ "com.system.app", true);
mTestableLooper.processAllMessages();
@@ -328,7 +341,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "com.google.android.gms", false);
+ "com.system.app", false);
mTestableLooper.processAllMessages();
verify(callback, times(1)).onLocationActiveChanged(false);
@@ -354,4 +367,4 @@ public class LocationControllerImplTest extends SysuiTestCase {
// No new callbacks
verify(callback).onLocationSettingsChanged(anyBoolean());
}
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 8ad6271bfc7e..2be67edfc946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -43,6 +43,10 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
}
@Override
+ public void refreshIconGroup(IconManager iconManager) {
+ }
+
+ @Override
public void setExternalIcon(String slot) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 593b97e55739..82880febc7f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1221,7 +1221,7 @@ public class BubblesTest extends SysuiTestCase {
final Context userContext = setUpContextWithPackageManager(workPkg,
mock(ApplicationInfo.class));
- // If things are working correctly, StatusBar.getPackageManagerForUser will call this
+ // If things are working correctly, CentralSurfaces.getPackageManagerForUser will call this
when(context.createPackageContextAsUser(eq(workPkg), anyInt(), eq(workUser)))
.thenReturn(userContext);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0e9926590511..0ea087d6de0f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2749,6 +2749,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void updateWindowMagnificationConnectionIfNeeded(AccessibilityUserState userState) {
+ if (!mMagnificationController.supportWindowMagnification()) {
+ return;
+ }
final boolean connect = (userState.isShortcutMagnificationEnabledLocked()
|| userState.isDisplayMagnificationEnabledLocked())
&& (userState.getMagnificationCapabilitiesLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index a95820966926..fa32452f389e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -303,7 +303,7 @@ public class FullScreenMagnificationController implements
public void onImeWindowVisibilityChanged(boolean shown) {
final Message m = PooledLambda.obtainMessage(
FullScreenMagnificationController::notifyImeWindowVisibilityChanged,
- FullScreenMagnificationController.this, shown);
+ FullScreenMagnificationController.this, mDisplayId, shown);
mControllerCtx.getHandler().sendMessage(m);
}
@@ -1215,11 +1215,12 @@ public class FullScreenMagnificationController implements
/**
* Notifies that the IME window visibility changed.
*
+ * @param displayId the logical display id
* @param shown {@code true} means the IME window shows on the screen. Otherwise it's
* hidden.
*/
- void notifyImeWindowVisibilityChanged(boolean shown) {
- mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(shown);
+ void notifyImeWindowVisibilityChanged(int displayId, boolean shown) {
+ mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(displayId, shown);
}
/**
@@ -1609,17 +1610,19 @@ public class FullScreenMagnificationController implements
* Called when the state of the magnification activation is changed.
* It is for the logging data of the magnification activation state.
*
- * @param displayId The logical display id.
+ * @param displayId the logical display id
* @param activated {@code true} if the magnification is activated, otherwise {@code false}.
*/
void onFullScreenMagnificationActivationState(int displayId, boolean activated);
/**
* Called when the IME window visibility changed.
+ *
+ * @param displayId the logical display id
* @param shown {@code true} means the IME window shows on the screen. Otherwise it's
* hidden.
*/
- void onImeWindowVisibilityChanged(boolean shown);
+ void onImeWindowVisibilityChanged(int displayId, boolean shown);
/**
* Called when the magnification spec changed.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 09e82c787c90..62ba0c821f4e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility.magnification;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
+import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -38,6 +39,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -91,6 +93,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private FullScreenMagnificationController mFullScreenMagnificationController;
private WindowMagnificationManager mWindowMagnificationMgr;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ /** Whether the platform supports window magnification feature. */
+ private final boolean mSupportWindowMagnification;
@GuardedBy("mLock")
private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -99,7 +103,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
// Track the active user to reset the magnification and get the associated user settings.
private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
@GuardedBy("mLock")
- private boolean mImeWindowVisible = false;
+ private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
private long mWindowModeEnabledTime = 0;
private long mFullScreenModeEnabledTime = 0;
@@ -129,6 +133,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mScaleProvider = scaleProvider;
LocalServices.getService(WindowManagerInternal.class)
.getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
+ mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
+ FEATURE_WINDOW_MAGNIFICATION);
}
@VisibleForTesting
@@ -185,6 +191,11 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
}
+ /** Returns {@code true} if the platform supports window magnification feature. */
+ public boolean supportWindowMagnification() {
+ return mSupportWindowMagnification;
+ }
+
/**
* Transitions to the target Magnification mode with current center of the magnification mode
* if it is available.
@@ -377,7 +388,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
mLastActivatedMode = mActivatedMode;
}
- logMagnificationModeWithImeOnIfNeeded();
+ logMagnificationModeWithImeOnIfNeeded(displayId);
disableFullScreenMagnificationIfNeeded(displayId);
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
@@ -432,7 +443,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mLastActivatedMode = mActivatedMode;
}
- logMagnificationModeWithImeOnIfNeeded();
+ logMagnificationModeWithImeOnIfNeeded(displayId);
disableWindowMagnificationIfNeeded(displayId);
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
@@ -454,12 +465,12 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
@Override
- public void onImeWindowVisibilityChanged(boolean shown) {
+ public void onImeWindowVisibilityChanged(int displayId, boolean shown) {
synchronized (mLock) {
- mImeWindowVisible = shown;
+ mIsImeVisibleArray.put(displayId, shown);
}
- getWindowMagnificationMgr().onImeWindowVisibilityChanged(shown);
- logMagnificationModeWithImeOnIfNeeded();
+ getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown);
+ logMagnificationModeWithImeOnIfNeeded(displayId);
}
/**
@@ -575,11 +586,12 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
}
- private void logMagnificationModeWithImeOnIfNeeded() {
+ private void logMagnificationModeWithImeOnIfNeeded(int displayId) {
final int mode;
synchronized (mLock) {
- if (!mImeWindowVisible || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+ if (!mIsImeVisibleArray.get(displayId, false)
+ || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
return;
}
mode = mActivatedMode;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 175182c7a3bb..3e07b095fd29 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -396,6 +396,10 @@ public class MagnificationProcessor {
dumpTrackingTypingFocusEnabledState(pw, displayId, config.getMode());
}
+ pw.append(" SupportWindowMagnification="
+ + mController.supportWindowMagnification()).println();
+ pw.append(" WindowMagnificationConnectionState="
+ + mController.getWindowMagnificationMgr().getConnectionState()).println();
}
private int getIdOfLastServiceToMagnify(int mode, int displayId) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 25dcc2aea41b..041eece5ce48 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -133,6 +133,25 @@ class WindowMagnificationConnectionWrapper {
return true;
}
+ boolean moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
+ @Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".moveWindowMagnifierToPosition",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId
+ + ";positionX=" + positionX + ";positionY=" + positionY);
+ }
+ try {
+ mConnection.moveWindowMagnifierToPosition(displayId, positionX, positionY,
+ transformToRemoteCallback(callback, mTrace));
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling moveWindowMagnifierToPosition()", e);
+ }
+ return false;
+ }
+ return true;
+ }
+
boolean showMagnificationButton(int displayId, int magnificationMode) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".showMagnificationButton",
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 89910eac06c5..aeb1112ede5f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -36,8 +36,10 @@ import android.graphics.Region;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.MotionEvent;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -87,6 +89,30 @@ public class WindowMagnificationManager implements
})
public @interface WindowPosition {}
+ /** Window magnification connection is connecting. */
+ private static final int CONNECTING = 0;
+ /** Window magnification connection is connected. */
+ private static final int CONNECTED = 1;
+ /** Window magnification connection is disconnecting. */
+ private static final int DISCONNECTING = 2;
+ /** Window magnification connection is disconnected. */
+ private static final int DISCONNECTED = 3;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_STATE"}, value = {
+ CONNECTING,
+ CONNECTED,
+ DISCONNECTING,
+ DISCONNECTED
+ })
+ private @interface ConnectionState {
+ }
+
+ @ConnectionState
+ private int mConnectionState = DISCONNECTED;
+
+ private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100;
+
private final Object mLock;
private final Context mContext;
@VisibleForTesting
@@ -99,6 +125,7 @@ public class WindowMagnificationManager implements
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
+ private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
private boolean mReceiverRegistered = false;
@VisibleForTesting
@@ -178,7 +205,7 @@ public class WindowMagnificationManager implements
*/
public void setConnection(@Nullable IWindowMagnificationConnection connection) {
if (DBG) {
- Slog.d(TAG, "setConnection :" + connection);
+ Slog.d(TAG, "setConnection :" + connection + " ,mConnectionState=" + mConnectionState);
}
synchronized (mLock) {
// Reset connectionWrapper.
@@ -189,6 +216,13 @@ public class WindowMagnificationManager implements
}
mConnectionWrapper.unlinkToDeath(mConnectionCallback);
mConnectionWrapper = null;
+ // The connection is still connecting so it is no need to reset the
+ // connection state to disconnected.
+ // TODO b/220086369 will reset the connection immediately when requestConnection
+ // is called
+ if (mConnectionState != CONNECTING) {
+ setConnectionState(DISCONNECTED);
+ }
}
if (connection != null) {
mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
@@ -199,9 +233,13 @@ public class WindowMagnificationManager implements
mConnectionCallback = new ConnectionCallback();
mConnectionWrapper.linkToDeath(mConnectionCallback);
mConnectionWrapper.setConnectionCallback(mConnectionCallback);
+ setConnectionState(CONNECTED);
} catch (RemoteException e) {
Slog.e(TAG, "setConnection failed", e);
mConnectionWrapper = null;
+ setConnectionState(DISCONNECTED);
+ } finally {
+ mLock.notify();
}
}
}
@@ -229,10 +267,20 @@ public class WindowMagnificationManager implements
if (DBG) {
Slog.d(TAG, "requestConnection :" + connect);
}
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ }
synchronized (mLock) {
- if (connect == isConnected()) {
+ if ((connect && (mConnectionState == CONNECTED || mConnectionState == CONNECTING))
+ || (!connect && (mConnectionState == DISCONNECTED
+ || mConnectionState == DISCONNECTING))) {
+ Slog.w(TAG,
+ "requestConnection duplicated request: connect=" + connect
+ + " ,mConnectionState=" + mConnectionState);
return false;
}
+
if (connect) {
final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
if (!mReceiverRegistered) {
@@ -247,19 +295,42 @@ public class WindowMagnificationManager implements
}
}
}
- if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
- mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
- FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ if (requestConnectionInternal(connect)) {
+ setConnectionState(connect ? CONNECTING : DISCONNECTING);
+ return true;
+ } else {
+ setConnectionState(DISCONNECTED);
+ return false;
}
+ }
+
+ private boolean requestConnectionInternal(boolean connect) {
final long identity = Binder.clearCallingIdentity();
try {
final StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
- service.requestWindowMagnificationConnection(connect);
+ if (service != null) {
+ return service.requestWindowMagnificationConnection(connect);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
- return true;
+ return false;
+ }
+
+ /**
+ * Returns window magnification connection state.
+ */
+ public int getConnectionState() {
+ return mConnectionState;
+ }
+
+ private void setConnectionState(@ConnectionState int state) {
+ if (DBG) {
+ Slog.d(TAG, "setConnectionState : state=" + state + " ,mConnectionState="
+ + mConnectionState);
+ }
+ mConnectionState = state;
}
/**
@@ -314,9 +385,13 @@ public class WindowMagnificationManager implements
float toCenterX = (float) (left + right) / 2;
float toCenterY = (float) (top + bottom) / 2;
- if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY)
- && isTrackingTypingFocusEnabled(displayId)) {
- enableWindowMagnification(displayId, Float.NaN, toCenterX, toCenterY);
+ synchronized (mLock) {
+ if (mIsImeVisibleArray.get(displayId, false)
+ && !isPositionInSourceBounds(displayId, toCenterX, toCenterY)
+ && isTrackingTypingFocusEnabled(displayId)) {
+ moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY,
+ STUB_ANIMATION_CALLBACK);
+ }
}
}
@@ -357,7 +432,7 @@ public class WindowMagnificationManager implements
* @param displayId The logical display id.
* @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus.
*/
- private void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
+ void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -384,7 +459,8 @@ public class WindowMagnificationManager implements
*
* @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden.
*/
- void onImeWindowVisibilityChanged(boolean shown) {
+ void onImeWindowVisibilityChanged(int displayId, boolean shown) {
+ mIsImeVisibleArray.put(displayId, shown);
if (shown) {
enableAllTrackingTypingFocus();
}
@@ -500,8 +576,11 @@ public class WindowMagnificationManager implements
animationCallback, windowPosition, id);
}
- if (enabled && !previousEnabled) {
- mCallback.onWindowMagnificationActivationState(displayId, true);
+ if (enabled) {
+ setTrackingTypingFocusEnabled(displayId, true);
+ if (!previousEnabled) {
+ mCallback.onWindowMagnificationActivationState(displayId, true);
+ }
}
return enabled;
}
@@ -563,14 +642,13 @@ public class WindowMagnificationManager implements
}
}
+ @GuardedBy("mLock")
boolean isPositionInSourceBounds(int displayId, float x, float y) {
- synchronized (mLock) {
- WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
- return false;
- }
- return magnifier.isPositionInSourceBounds(x, y);
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
}
+ return magnifier.isPositionInSourceBounds(x, y);
}
/**
@@ -829,10 +907,10 @@ public class WindowMagnificationManager implements
}
@Override
- public void onDrag(int displayId) {
+ public void onMove(int displayId) {
if (mTrace.isA11yTracingEnabledForTypes(
FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
- mTrace.logTrace(TAG + "ConnectionCallback.onDrag",
+ mTrace.logTrace(TAG + "ConnectionCallback.onMove",
FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
"displayId=" + displayId);
}
@@ -849,6 +927,7 @@ public class WindowMagnificationManager implements
mConnectionWrapper.unlinkToDeath(this);
mConnectionWrapper = null;
mConnectionCallback = null;
+ setConnectionState(DISCONNECTED);
resetWindowMagnifiers();
}
}
@@ -884,7 +963,6 @@ public class WindowMagnificationManager implements
mWindowMagnificationManager = windowMagnificationManager;
}
- @GuardedBy("mLock")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition, int id) {
@@ -962,7 +1040,6 @@ public class WindowMagnificationManager implements
return mIdOfLastServiceToControl;
}
- @GuardedBy("mLock")
int pointersInWindow(MotionEvent motionEvent) {
int count = 0;
final int pointerCount = motionEvent.getPointerCount();
@@ -1025,11 +1102,22 @@ public class WindowMagnificationManager implements
float centerY, float magnificationFrameOffsetRatioX,
float magnificationFrameOffsetRatioY,
MagnificationAnimationCallback animationCallback) {
+ // Wait for the connection with a timeout.
+ final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS;
+ while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) {
+ try {
+ mLock.wait(endMillis - SystemClock.uptimeMillis());
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
if (mConnectionWrapper == null) {
- Slog.w(TAG, "enableWindowMagnificationInternal mConnectionWrapper is null");
+ Slog.w(TAG,
+ "enableWindowMagnificationInternal mConnectionWrapper is null. "
+ + "mConnectionState=" + mConnectionState);
return false;
}
- return mConnectionWrapper.enableWindowMagnification(
+ return mConnectionWrapper.enableWindowMagnification(
displayId, scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
animationCallback);
@@ -1050,8 +1138,16 @@ public class WindowMagnificationManager implements
displayId, animationCallback);
}
+ @GuardedBy("mLock")
private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {
return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifier(
displayId, offsetX, offsetY);
}
+
+ @GuardedBy("mLock")
+ private boolean moveWindowMagnifierToPositionInternal(int displayId, float positionX,
+ float positionY, MagnificationAnimationCallback animationCallback) {
+ return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition(
+ displayId, positionX, positionY, animationCallback);
+ }
}
diff --git a/services/api/current.txt b/services/api/current.txt
index e46c97247681..440f66aaa76b 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -38,7 +38,7 @@ package com.android.server {
package com.android.server.am {
public interface ActivityManagerLocal {
- method public boolean bindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String, int) throws android.os.RemoteException;
+ method public boolean bindSdkSandboxService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String, int) throws android.os.RemoteException;
method public boolean canStartForegroundService(int, int, @NonNull String);
}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index daead0a5ff9d..b1f572d2a364 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -43,6 +43,8 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -62,7 +64,7 @@ public class CloudSearchManagerService extends
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
- R.string.config_defaultCloudSearchService), null,
+ R.array.config_defaultCloudSearchServices, true), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context;
@@ -70,7 +72,25 @@ public class CloudSearchManagerService extends
@Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
- return new CloudSearchPerUserService(this, mLock, resolvedUserId);
+ return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
+ }
+
+ @Override
+ protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
+ boolean disabled, String[] serviceNames) {
+ if (serviceNames == null) {
+ return new ArrayList<>();
+ }
+ List<CloudSearchPerUserService> serviceList =
+ new ArrayList<>(serviceNames.length);
+ for (int i = 0; i < serviceNames.length; i++) {
+ if (serviceNames[i] == null) {
+ continue;
+ }
+ serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
+ serviceNames[i]));
+ }
+ return serviceList;
}
@Override
@@ -111,19 +131,28 @@ public class CloudSearchManagerService extends
@NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setSource(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
- runForUserLocked("search", searchRequest.getRequestId(), (service) ->
- service.onSearchLocked(searchRequest, callBack));
+ runForUser("search", (service) -> {
+ synchronized (service.mLock) {
+ service.onSearchLocked(searchRequest, callBack);
+ }
+ });
}
@Override
public void returnResults(IBinder token, String requestId, SearchResponse response) {
- runForUserLocked("returnResults", requestId, (service) ->
- service.onReturnResultsLocked(token, requestId, response));
+ runForUser("returnResults", (service) -> {
+ synchronized (service.mLock) {
+ service.onReturnResultsLocked(token, requestId, response);
+ }
+ });
}
public void destroy(@NonNull SearchRequest searchRequest) {
- runForUserLocked("destroyCloudSearchSession", searchRequest.getRequestId(),
- (service) -> service.onDestroyLocked(searchRequest.getRequestId()));
+ runForUser("destroyCloudSearchSession", (service) -> {
+ synchronized (service.mLock) {
+ service.onDestroyLocked(searchRequest.getRequestId());
+ }
+ });
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -134,8 +163,7 @@ public class CloudSearchManagerService extends
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void runForUserLocked(@NonNull final String func,
- @NonNull final String requestId,
+ private void runForUser(@NonNull final String func,
@NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -143,7 +171,7 @@ public class CloudSearchManagerService extends
null, null);
if (DEBUG) {
- Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
@@ -160,8 +188,11 @@ public class CloudSearchManagerService extends
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- final CloudSearchPerUserService service = getServiceForUserLocked(userId);
- c.accept(service);
+ final List<CloudSearchPerUserService> services =
+ getServiceListForUserLocked(userId);
+ for (int i = 0; i < services.size(); i++) {
+ c.accept(services.get(i));
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
index 51f5fd9668cf..c64982d94d6d 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
@@ -54,7 +54,12 @@ public class CloudSearchManagerServiceShellCommand extends ShellCommand {
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
- mService.setTemporaryService(userId, serviceName, duration);
+ String[] services = serviceName.split(";");
+ if (services.length == 0) {
+ return 0;
+ } else {
+ mService.setTemporaryServices(userId, services, duration);
+ }
pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms");
break;
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
index 32d66afb33b9..116c7391b388 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
@@ -49,6 +49,8 @@ public class CloudSearchPerUserService extends
@GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE);
+ private final String mServiceName;
+ private final ComponentName mRemoteComponentName;
@Nullable
@GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService;
@@ -60,8 +62,10 @@ public class CloudSearchPerUserService extends
private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master,
- Object lock, int userId) {
+ Object lock, int userId, String serviceName) {
super(master, lock, userId);
+ mServiceName = serviceName;
+ mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
}
@Override // from PerUserSystemService
@@ -108,7 +112,7 @@ public class CloudSearchPerUserService extends
? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
- String remoteServicePackageName = getServiceComponentName().getPackageName();
+ String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted.
boolean wantedProvider = true;
if (filterList.length() > 0) {
@@ -150,11 +154,19 @@ public class CloudSearchPerUserService extends
/**
* Used to return results back to the clients.
*/
+ @GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId,
@NonNull SearchResponse response) {
+ if (mRemoteService == null) {
+ return;
+ }
+ ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
+ if (serviceInterface == null || token != serviceInterface.asBinder()) {
+ return;
+ }
if (mCallbackQueue.containsKey(requestId)) {
- response.setSource(mRemoteService.getComponentName().getPackageName());
+ response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
@@ -163,6 +175,10 @@ public class CloudSearchPerUserService extends
sessionInfo.mCallback.onSearchFailed(response);
}
} catch (RemoteException e) {
+ if (mMaster.debug) {
+ Slog.e(TAG, "Exception in posting results");
+ e.printStackTrace();
+ }
onDestroyLocked(requestId);
}
}
@@ -297,7 +313,7 @@ public class CloudSearchPerUserService extends
@Nullable
private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) {
- final String serviceName = getComponentNameLocked();
+ final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set");
diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
index 2c42c910b9ab..adc8459de658 100644
--- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
+++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
@@ -33,17 +33,19 @@ import com.android.internal.annotations.GuardedBy;
/**
* Handles blocking access to the camera for apps running on virtual devices.
*/
-class CameraAccessController extends CameraManager.AvailabilityCallback {
+class CameraAccessController extends CameraManager.AvailabilityCallback implements AutoCloseable {
private static final String TAG = "CameraAccessController";
private final Object mLock = new Object();
private final Context mContext;
- private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
- CameraAccessBlockedCallback mBlockedCallback;
- private CameraManager mCameraManager;
- private boolean mListeningForCameraEvents;
- private PackageManager mPackageManager;
+ private final VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
+ private final CameraAccessBlockedCallback mBlockedCallback;
+ private final CameraManager mCameraManager;
+ private final PackageManager mPackageManager;
+
+ @GuardedBy("mLock")
+ private int mObserverCount = 0;
@GuardedBy("mLock")
private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>();
@@ -77,21 +79,36 @@ class CameraAccessController extends CameraManager.AvailabilityCallback {
*/
public void startObservingIfNeeded() {
synchronized (mLock) {
- if (!mListeningForCameraEvents) {
+ if (mObserverCount == 0) {
mCameraManager.registerAvailabilityCallback(mContext.getMainExecutor(), this);
- mListeningForCameraEvents = true;
}
+ mObserverCount++;
}
}
/**
* Stop watching for camera access.
*/
- public void stopObserving() {
+ public void stopObservingIfNeeded() {
+ synchronized (mLock) {
+ mObserverCount--;
+ if (mObserverCount <= 0) {
+ close();
+ }
+ }
+ }
+
+
+ @Override
+ public void close() {
synchronized (mLock) {
- mCameraManager.unregisterAvailabilityCallback(this);
- mListeningForCameraEvents = false;
+ if (mObserverCount < 0) {
+ Slog.wtf(TAG, "Unexpected negative mObserverCount: " + mObserverCount);
+ } else if (mObserverCount > 0) {
+ Slog.w(TAG, "Unexpected close with observers remaining: " + mObserverCount);
+ }
}
+ mCameraManager.unregisterAvailabilityCallback(this);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index bc1f28d1c373..b991ba87eef4 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -24,6 +24,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.VirtualDeviceParams.ActivityPolicy;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
@@ -77,7 +79,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@Nullable
private final ArraySet<ComponentName> mBlockedActivities;
private final Object mGenericWindowPolicyControllerLock = new Object();
- private Consumer<ActivityInfo> mActivityBlockedCallback;
+ @ActivityPolicy
+ private final int mDefaultActivityPolicy;
+ private final Consumer<ActivityInfo> mActivityBlockedCallback;
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -95,18 +99,30 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
* @param windowFlags The window flags that this controller is interested in.
* @param systemWindowFlags The system window flags that this controller is interested in.
* @param allowedUsers The set of users that are allowed to stream in this display.
+ * @param allowedActivities The set of activities explicitly allowed to stream on this device.
+ * Used only if the {@code activityPolicy} is
+ * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_BLOCKED}.
+ * @param blockedActivities The set of activities explicitly blocked from streaming on this
+ * device. Used only if the {@code activityPolicy} is
+ * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED}
+ * @param defaultActivityPolicy Whether activities are default allowed to be displayed or
+ * blocked.
* @param activityListener Activity listener to listen for activity changes. The display ID
* is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
+ * @param activityBlockedCallback Callback that is called when an activity is blocked from
+ * launching.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@NonNull ArraySet<UserHandle> allowedUsers,
- @Nullable Set<ComponentName> allowedActivities,
- @Nullable Set<ComponentName> blockedActivities,
+ @NonNull Set<ComponentName> allowedActivities,
+ @NonNull Set<ComponentName> blockedActivities,
+ @ActivityPolicy int defaultActivityPolicy,
@NonNull ActivityListener activityListener,
@NonNull Consumer<ActivityInfo> activityBlockedCallback) {
mAllowedUsers = allowedUsers;
- mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
- mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+ mAllowedActivities = new ArraySet<>(allowedActivities);
+ mBlockedActivities = new ArraySet<>(blockedActivities);
+ mDefaultActivityPolicy = defaultActivityPolicy;
mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
@@ -191,11 +207,13 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser);
return false;
}
- if (mBlockedActivities != null && mBlockedActivities.contains(activityComponent)) {
+ if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED
+ && mBlockedActivities.contains(activityComponent)) {
Slog.d(TAG, "Virtual device blocking launch of " + activityComponent);
return false;
}
- if (mAllowedActivities != null && !mAllowedActivities.contains(activityComponent)) {
+ if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_BLOCKED
+ && !mAllowedActivities.contains(activityComponent)) {
Slog.d(TAG, activityComponent + " is not in the allowed list.");
return false;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java b/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
index c397ea2997a4..77b880f6e62f 100644
--- a/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
+++ b/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
@@ -18,6 +18,8 @@ package com.android.server.companion.virtual;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.UserHandle;
import android.util.Slog;
/**
@@ -32,13 +34,15 @@ class PermissionUtils {
*
* @param context the context
* @param callingPackage the calling application package name
- * @param callingUid the calling application uid
- * @return {@code true} if the package name matches the calling app uid, {@code false} otherwise
+ * @return {@code true} if the package name matches {@link Binder#getCallingUid()}, or
+ * {@code false} otherwise
*/
- public static boolean validatePackageName(Context context, String callingPackage,
- int callingUid) {
+ public static boolean validateCallingPackageName(Context context, String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
try {
- int packageUid = context.getPackageManager().getPackageUid(callingPackage, 0);
+ int packageUid = context.getPackageManager()
+ .getPackageUidAsUser(callingPackage, UserHandle.getUserId(callingUid));
if (packageUid != callingUid) {
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
+ " is UID " + packageUid + " but caller is " + callingUid);
@@ -48,6 +52,8 @@ class PermissionUtils {
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
+ " does not exist");
return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return true;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index c0a904fe3d9a..b05a7dbe83d1 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -140,7 +140,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
int ownerUid, InputController inputController, OnDeviceCloseListener listener,
PendingTrampolineCallback pendingTrampolineCallback,
IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) {
- mContext = context;
+ UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
+ mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -505,6 +506,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
getAllowedUserHandles(),
mParams.getAllowedActivities(),
mParams.getBlockedActivities(),
+ mParams.getDefaultActivityPolicy(),
createListenerAdapter(displayId),
activityInfo -> onActivityBlocked(displayId, activityInfo));
mWindowPolicyControllers.put(displayId, dwpc);
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index c7d8daac606b..9f252d744144 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -66,7 +67,12 @@ public class VirtualDeviceManagerService extends SystemService {
private VirtualDeviceManagerInternal mLocalService;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
- private final CameraAccessController mCameraAccessController;
+ /**
+ * Mapping from user IDs to CameraAccessControllers.
+ */
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private final SparseArray<CameraAccessController> mCameraAccessControllers =
+ new SparseArray<>();
/**
* Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
@@ -94,8 +100,6 @@ public class VirtualDeviceManagerService extends SystemService {
super(context);
mImpl = new VirtualDeviceManagerImpl();
mLocalService = new LocalService();
- mCameraAccessController = new CameraAccessController(getContext(), mLocalService,
- this::onCameraAccessBlocked);
}
private final ActivityInterceptorCallback mActivityInterceptorCallback =
@@ -144,16 +148,19 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public void onUserStarting(@NonNull TargetUser user) {
super.onUserStarting(user);
+ Context userContext = getContext().createContextAsUser(user.getUserHandle(), 0);
synchronized (mVirtualDeviceManagerLock) {
- final CompanionDeviceManager cdm = getContext()
- .createContextAsUser(user.getUserHandle(), 0)
- .getSystemService(CompanionDeviceManager.class);
+ final CompanionDeviceManager cdm =
+ userContext.getSystemService(CompanionDeviceManager.class);
final int userId = user.getUserIdentifier();
mAllAssociations.put(userId, cdm.getAllAssociations());
OnAssociationsChangedListener listener =
associations -> mAllAssociations.put(userId, associations);
mOnAssociationsChangedListeners.put(userId, listener);
cdm.addOnAssociationsChangedListener(Runnable::run, listener);
+ CameraAccessController cameraAccessController = new CameraAccessController(
+ userContext, mLocalService, this::onCameraAccessBlocked);
+ mCameraAccessControllers.put(user.getUserIdentifier(), cameraAccessController);
}
}
@@ -171,6 +178,14 @@ public class VirtualDeviceManagerService extends SystemService {
cdm.removeOnAssociationsChangedListener(listener);
mOnAssociationsChangedListeners.remove(userId);
}
+ CameraAccessController cameraAccessController = mCameraAccessControllers.get(
+ user.getUserIdentifier());
+ if (cameraAccessController != null) {
+ cameraAccessController.close();
+ mCameraAccessControllers.remove(user.getUserIdentifier());
+ } else {
+ Slog.w(TAG, "Cannot unregister cameraAccessController for user " + user);
+ }
}
}
@@ -198,7 +213,7 @@ public class VirtualDeviceManagerService extends SystemService {
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"createVirtualDevice");
final int callingUid = getCallingUid();
- if (!PermissionUtils.validatePackageName(getContext(), packageName, callingUid)) {
+ if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
throw new SecurityException(
"Package name " + packageName + " does not belong to calling uid "
+ callingUid);
@@ -213,6 +228,9 @@ public class VirtualDeviceManagerService extends SystemService {
"Virtual device for association ID " + associationId
+ " already exists");
}
+ final int userId = UserHandle.getUserId(callingUid);
+ final CameraAccessController cameraAccessController =
+ mCameraAccessControllers.get(userId);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
associationInfo, token, callingUid,
new VirtualDeviceImpl.OnDeviceCloseListener() {
@@ -220,14 +238,21 @@ public class VirtualDeviceManagerService extends SystemService {
public void onClose(int associationId) {
synchronized (mVirtualDeviceManagerLock) {
mVirtualDevices.remove(associationId);
- if (mVirtualDevices.size() == 0) {
- mCameraAccessController.stopObserving();
+ if (cameraAccessController != null) {
+ cameraAccessController.stopObservingIfNeeded();
+ } else {
+ Slog.w(TAG, "cameraAccessController not found for user "
+ + userId);
}
}
}
},
this, activityListener, params);
- mCameraAccessController.startObservingIfNeeded();
+ if (cameraAccessController != null) {
+ cameraAccessController.startObservingIfNeeded();
+ } else {
+ Slog.w(TAG, "cameraAccessController not found for user " + userId);
+ }
mVirtualDevices.put(associationInfo.getId(), virtualDevice);
return virtualDevice;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a2b289c6e8b9..0241c1e96a2f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -133,7 +133,7 @@ java_library_static {
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
"service-permission.stubs.system_server",
- "service-supplementalprocess.stubs.system_server",
+ "service-sdksandbox.stubs.system_server",
],
required: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 111bd340a3d1..e6953f0032c7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -657,6 +657,11 @@ public abstract class PackageManagerInternal {
public abstract void notifyPackageUse(String packageName, int reason);
/**
+ * Notify the package is force stopped.
+ */
+ public abstract void onPackageProcessKilledForUninstall(String packageName);
+
+ /**
* Returns a package object for the given package name.
*/
public abstract @Nullable AndroidPackage getPackage(@NonNull String packageName);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 6986d3bbe585..1f8ef8226c32 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -18,14 +18,22 @@ package com.android.server;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemProperties;
@@ -42,6 +50,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/**
@@ -49,6 +58,7 @@ import java.util.stream.Collectors;
*/
public class BinaryTransparencyService extends SystemService {
private static final String TAG = "TransparencyService";
+ private static final String EXTRA_SERVICE = "service";
@VisibleForTesting
static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
@@ -365,10 +375,80 @@ public class BinaryTransparencyService extends SystemService {
// we are only interested in doing things at PHASE_BOOT_COMPLETED
if (phase == PHASE_BOOT_COMPLETED) {
- // due to potentially long computation that holds up boot time, apex sha computations
- // are deferred to first call
Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
getVBMetaDigestInformation();
+
+ // due to potentially long computation that holds up boot time, computations for
+ // SHA256 digests of APEX and Module packages are scheduled here,
+ // but only executed when device is idle.
+ Slog.i(TAG, "Scheduling APEX and Module measurements to be updated.");
+ UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
+ BinaryTransparencyService.this);
+ }
+ }
+
+ /**
+ * JobService to update binary measurements and update internal cache.
+ */
+ public static class UpdateMeasurementsJobService extends JobService {
+ private static final int COMPUTE_APEX_MODULE_SHA256_JOB_ID =
+ BinaryTransparencyService.UpdateMeasurementsJobService.class.hashCode();
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Slog.d(TAG, "Job to update binary measurements started.");
+ if (params.getJobId() != COMPUTE_APEX_MODULE_SHA256_JOB_ID) {
+ return false;
+ }
+
+ // we'll still update the measurements via threads to be mindful of low-end devices
+ // where this operation might take longer than expected, and so that we don't block
+ // system_server's main thread.
+ Executors.defaultThreadFactory().newThread(() -> {
+ // since we can't call updateBinaryMeasurements() directly, calling
+ // getApexInfo() achieves the same effect, and we simply discard the return
+ // value
+
+ IBinder b = ServiceManager.getService(Context.BINARY_TRANSPARENCY_SERVICE);
+ IBinaryTransparencyService iBtsService =
+ IBinaryTransparencyService.Stub.asInterface(b);
+ try {
+ iBtsService.getApexInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Updating binary measurements was interrupted.", e);
+ return;
+ }
+ jobFinished(params, false);
+ }).start();
+
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return false;
+ }
+
+ @SuppressLint("DefaultLocale")
+ static void scheduleBinaryMeasurements(Context context, BinaryTransparencyService service) {
+ Slog.i(TAG, "Scheduling APEX & Module SHA256 digest computation job");
+ final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler == null) {
+ Slog.e(TAG, "Failed to obtain an instance of JobScheduler.");
+ return;
+ }
+
+ final JobInfo jobInfo = new JobInfo.Builder(COMPUTE_APEX_MODULE_SHA256_JOB_ID,
+ new ComponentName(context, UpdateMeasurementsJobService.class))
+ .setRequiresDeviceIdle(true)
+ .build();
+ if (jobScheduler.schedule(jobInfo) != JobScheduler.RESULT_SUCCESS) {
+ Slog.e(TAG, "Failed to schedule job to update binary measurements.");
+ return;
+ }
+ Slog.d(TAG, String.format(
+ "Job %d to update binary measurements scheduled successfully.",
+ COMPUTE_APEX_MODULE_SHA256_JOB_ID));
}
}
@@ -380,7 +460,7 @@ public class BinaryTransparencyService extends SystemService {
@NonNull
private List<PackageInfo> getInstalledApexs() {
- List<PackageInfo> results = new ArrayList<PackageInfo>();
+ List<PackageInfo> results = new ArrayList<>();
PackageManager pm = mContext.getPackageManager();
if (pm == null) {
Slog.e(TAG, "Error obtaining an instance of PackageManager.");
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index ff2308c35b9f..fcde533fe05c 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.timedetector.NetworkTimeSuggestion;
@@ -38,6 +39,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.os.TimestampedValue;
import android.provider.Settings;
+import android.util.LocalLog;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.TimeUtils;
@@ -95,6 +97,13 @@ public class NetworkTimeUpdateService extends Binder {
// connection to happen.
private int mTryAgainCounter;
+ /**
+ * A log that records the decisions to fetch a network time update.
+ * This is logged in bug reports to assist with debugging issues with network time suggestions.
+ */
+ @NonNull
+ private final LocalLog mLocalLog = new LocalLog(30, false /* useLocalTimestamps */);
+
public NetworkTimeUpdateService(Context context) {
mContext = context;
mTime = NtpTrustedTime.getInstance(context);
@@ -155,17 +164,34 @@ public class NetworkTimeUpdateService extends Binder {
}
private void onPollNetworkTimeUnderWakeLock(int event) {
+ long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime();
// Force an NTP fix when outdated
NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
- if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
+ if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)
+ >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
- mTime.forceRefresh();
+ boolean isSuccessful = mTime.forceRefresh();
+ if (isSuccessful) {
+ mTryAgainCounter = 0;
+ } else {
+ String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult
+ + ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis;
+
+ if (DBG) {
+ Log.d(TAG, logMsg);
+ }
+ mLocalLog.log(logMsg);
+ }
+
cachedNtpResult = mTime.getCachedTimeResult();
}
- if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
+ if (cachedNtpResult != null
+ && cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)
+ < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
- resetAlarm(mPollingIntervalMs);
+ resetAlarm(mPollingIntervalMs
+ - cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis));
// Suggest the time to the time detector. It may choose use it to set the system clock.
TimestampedValue<Long> timeSignal = new TimestampedValue<>(
@@ -180,6 +206,11 @@ public class NetworkTimeUpdateService extends Binder {
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
+ String logMsg = "mTryAgainTimesMax exceeded, cachedNtpResult=" + cachedNtpResult;
+ if (DBG) {
+ Log.d(TAG, logMsg);
+ }
+ mLocalLog.log(logMsg);
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
@@ -285,6 +316,8 @@ public class NetworkTimeUpdateService extends Binder {
if (ntpResult != null) {
pw.println("NTP result age: " + ntpResult.getAgeMillis());
}
+ pw.println("Local logs:");
+ mLocalLog.dump(fd, pw, args);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 4129feb4e082..6ff8e36a1f82 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -21,14 +21,13 @@ per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *Battery* = file:/BATTERY_STATS_OWNERS
per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
-per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file **IpSec* = file:/services/core/java/com/android/server/net/OWNERS
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
-per-file *TimeUpdate* = file:/core/java/android/app/timezone/OWNERS
+per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
per-file MmsServiceBroker.java = file:/telephony/OWNERS
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c194527d54a5..d4764a61566e 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4747,7 +4747,7 @@ class StorageManagerService extends IStorageManager.Stub
private int getMountModeInternal(int uid, String packageName) {
try {
// Get some easy cases out of the way first
- if (Process.isIsolated(uid) || Process.isSupplemental(uid)) {
+ if (Process.isIsolated(uid) || Process.isSdkSandboxUid(uid)) {
return StorageManager.MOUNT_MODE_EXTERNAL_NONE;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 092172a15861..d4ad718fbe73 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2721,7 +2721,7 @@ public final class ActiveServices {
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
- String instanceName, boolean isSupplementalProcessService, int supplementedAppUid,
+ String instanceName, boolean isSdkSandboxService, int sdkSandboxClientAppUid,
String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
@@ -2807,7 +2807,7 @@ public final class ActiveServices {
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
- isSupplementalProcessService, supplementedAppUid, resolvedType, callingPackage,
+ isSdkSandboxService, sdkSandboxClientAppUid, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
@@ -3234,13 +3234,13 @@ public final class ActiveServices {
}
private ServiceLookupResult retrieveServiceLocked(Intent service,
- String instanceName, boolean isSupplementalProcessService, int supplementedAppUid,
+ String instanceName, boolean isSdkSandboxService, int sdkSandboxClientAppUid,
String resolvedType,
String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
- if (isSupplementalProcessService && instanceName == null) {
- throw new IllegalArgumentException("No instanceName provided for supplemental process");
+ if (isSdkSandboxService && instanceName == null) {
+ throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
}
ServiceRecord r = null;
@@ -3319,13 +3319,13 @@ public final class ActiveServices {
}
if (instanceName != null
&& (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0
- && !isSupplementalProcessService) {
+ && !isSdkSandboxService) {
throw new IllegalArgumentException("Can't use instance name '" + instanceName
- + "' with non-isolated non-supplemental service '" + sInfo.name + "'");
+ + "' with non-isolated non-sdk sandbox service '" + sInfo.name + "'");
}
- if (isSupplementalProcessService
+ if (isSdkSandboxService
&& (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
- throw new IllegalArgumentException("Service cannot be both supplemental and "
+ throw new IllegalArgumentException("Service cannot be both sdk sandbox and "
+ "isolated");
}
@@ -3412,11 +3412,11 @@ public final class ActiveServices {
final Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
- String supplementalProcessName = isSupplementalProcessService ? instanceName
+ String sdkSandboxProcessName = isSdkSandboxService ? instanceName
: null;
r = new ServiceRecord(mAm, className, name, definingPackageName,
definingUid, filter, sInfo, callingFromFg, res,
- supplementalProcessName, supplementedAppUid);
+ sdkSandboxProcessName, sdkSandboxClientAppUid);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
@@ -4139,7 +4139,8 @@ public final class ActiveServices {
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
- HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
+ HostingRecord hostingRecord = new HostingRecord("service", r.instanceName,
+ r.definingPackageName, r.definingUid, r.serviceInfo.processName);
ProcessRecord app;
if (!isolated) {
@@ -4177,11 +4178,12 @@ public final class ActiveServices {
app = r.isolationHostProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
- hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
+ hostingRecord = HostingRecord.byWebviewZygote(r.instanceName, r.definingPackageName,
+ r.definingUid, r.serviceInfo.processName);
}
if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
- r.definingUid);
+ r.definingUid, r.serviceInfo.processName);
}
}
@@ -4190,9 +4192,9 @@ public final class ActiveServices {
if (app == null && !permissionsReviewRequired && !packageFrozen) {
// TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
// was initiated from a notification tap or not.
- if (r.supplemental) {
- final int uid = Process.toSupplementalUid(r.supplementedAppUid);
- app = mAm.startSupplementalProcessLocked(procName, r.appInfo, true, intentFlags,
+ if (r.isSdkSandbox) {
+ final int uid = Process.toSdkSandboxUid(r.sdkSandboxClientAppUid);
+ app = mAm.startSdkSandboxProcessLocked(procName, r.appInfo, true, intentFlags,
hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, uid);
r.isolationHostProc = app;
} else {
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index 535340b4c9c1..3226a2b58a96 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -65,15 +65,15 @@ public interface ActivityManagerLocal {
void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs);
/**
- * Binds to a supplemental process service, creating it if needed. You can through the arguments
+ * Binds to a sdk sandbox service, creating it if needed. You can through the arguments
* here have the system bring up multiple concurrent processes hosting their own instance of
* that service. The {@code processName} you provide here identifies the different instances.
*
- * @param service Identifies the supplemental process service to connect to. The Intent must
+ * @param service Identifies the sdk sandbox process service to connect to. The Intent must
* specify an explicit component name. This value cannot be null.
* @param conn Receives information as the service is started and stopped.
* This must be a valid ServiceConnection object; it must not be null.
- * @param userAppUid Uid of the app for which the supplemental process needs to be spawned.
+ * @param clientAppUid Uid of the app for which the sdk sandbox process needs to be spawned.
* @param processName Unique identifier for the service instance. Each unique name here will
* result in a different service instance being created. Identifiers must only contain
* ASCII letters, digits, underscores, and periods.
@@ -86,7 +86,7 @@ public interface ActivityManagerLocal {
* @see Context#bindService(Intent, ServiceConnection, int)
*/
@SuppressLint("RethrowRemoteException")
- boolean bindSupplementalProcessService(@NonNull Intent service, @NonNull ServiceConnection conn,
- int userAppUid, @NonNull String processName, @Context.BindServiceFlags int flags)
+ boolean bindSdkSandboxService(@NonNull Intent service, @NonNull ServiceConnection conn,
+ int clientAppUid, @NonNull String processName, @Context.BindServiceFlags int flags)
throws RemoteException;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 20a850943577..47f9fd535801 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2789,13 +2789,13 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@GuardedBy("this")
- final ProcessRecord startSupplementalProcessLocked(String processName,
+ final ProcessRecord startSdkSandboxProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
- HostingRecord hostingRecord, int zygotePolicyFlags, int supplementalUid) {
+ HostingRecord hostingRecord, int zygotePolicyFlags, int sdkSandboxUid) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, false /* allowWhileBooting */,
false /* isolated */, 0 /* isolatedUid */,
- true /* supplemental */, supplementalUid,
+ true /* isSdkSandbox */, sdkSandboxUid,
null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
@@ -2807,7 +2807,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean isolated) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
- false /* supplemental */, 0 /* supplementalUid */,
+ false /* isSdkSandbox */, 0 /* sdkSandboxClientdAppUid */,
null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
@@ -12396,7 +12396,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
- boolean isSupplementalProcessService, int supplementedAppUid, String callingPackage,
+ boolean isSdkSandboxService, int sdkSandboxClientdAppUid, String callingPackage,
int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
@@ -12410,7 +12410,7 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("callingPackage cannot be null");
}
- if (isSupplementalProcessService && instanceName == null) {
+ if (isSdkSandboxService && instanceName == null) {
throw new IllegalArgumentException("No instance name provided for isolated process");
}
@@ -12426,10 +12426,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- synchronized(this) {
- return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
- flags, instanceName, isSupplementalProcessService, supplementedAppUid,
- callingPackage, userId);
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ final ComponentName cn = service.getComponent();
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindService:"
+ + (cn != null ? cn.toShortString() : service.getAction()));
+ }
+ synchronized (this) {
+ return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
+ flags, instanceName, isSdkSandboxService, sdkSandboxClientdAppUid,
+ callingPackage, userId);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -13586,6 +13595,8 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.getIntExtra(Intent.EXTRA_UID, -1)),
false, true, true, false, fullUninstall, userId,
removed ? "pkg removed" : "pkg changed");
+ getPackageManagerInternal()
+ .onPackageProcessKilledForUninstall(ssp);
} else {
// Kill any app zygotes always, since they can't fork new
// processes with references to the old code
@@ -16006,7 +16017,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean bindSupplementalProcessService(Intent service, ServiceConnection conn,
+ public boolean bindSdkSandboxService(Intent service, ServiceConnection conn,
int userAppUid, String processName, int flags) throws RemoteException {
if (service == null) {
throw new IllegalArgumentException("intent is null");
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index c1c3435f35ff..d07590fc8db5 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -398,23 +398,24 @@ public final class AppRestrictionController {
return sb.toString();
}
- @GuardedBy("mSettingsLock")
void dump(PrintWriter pw, @ElapsedRealtimeLong long nowElapsed) {
- pw.print(toString());
- if (mLastRestrictionLevel != RESTRICTION_LEVEL_UNKNOWN) {
- pw.print('/');
- pw.print(ActivityManager.restrictionLevelToName(mLastRestrictionLevel));
- }
- pw.print(" levelChange=");
- TimeUtils.formatDuration(mLevelChangeTimeElapsed - nowElapsed, pw);
- if (mLastNotificationShownTimeElapsed != null) {
- for (int i = 0; i < mLastNotificationShownTimeElapsed.length; i++) {
- if (mLastNotificationShownTimeElapsed[i] > 0) {
- pw.print(" lastNoti(");
- pw.print(mNotificationHelper.notificationTypeToString(i));
- pw.print(")=");
- TimeUtils.formatDuration(
- mLastNotificationShownTimeElapsed[i] - nowElapsed, pw);
+ synchronized (mSettingsLock) {
+ pw.print(toString());
+ if (mLastRestrictionLevel != RESTRICTION_LEVEL_UNKNOWN) {
+ pw.print('/');
+ pw.print(ActivityManager.restrictionLevelToName(mLastRestrictionLevel));
+ }
+ pw.print(" levelChange=");
+ TimeUtils.formatDuration(mLevelChangeTimeElapsed - nowElapsed, pw);
+ if (mLastNotificationShownTimeElapsed != null) {
+ for (int i = 0; i < mLastNotificationShownTimeElapsed.length; i++) {
+ if (mLastNotificationShownTimeElapsed[i] > 0) {
+ pw.print(" lastNoti(");
+ pw.print(mNotificationHelper.notificationTypeToString(i));
+ pw.print(")=");
+ TimeUtils.formatDuration(
+ mLastNotificationShownTimeElapsed[i] - nowElapsed, pw);
+ }
}
}
}
@@ -612,10 +613,11 @@ public final class AppRestrictionController {
}
}
- @GuardedBy("mSettingsLock")
- void dumpLocked(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix) {
final ArrayList<PkgSettings> settings = new ArrayList<>();
- mRestrictionLevels.forEach(setting -> settings.add(setting));
+ synchronized (mSettingsLock) {
+ mRestrictionLevels.forEach(setting -> settings.add(setting));
+ }
Collections.sort(settings, Comparator.comparingInt(PkgSettings::getUid));
final long nowElapsed = SystemClock.elapsedRealtime();
for (int i = 0, size = settings.size(); i < size; i++) {
@@ -1322,9 +1324,7 @@ public final class AppRestrictionController {
prefix = " " + prefix;
pw.print(prefix);
pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
- synchronized (mSettingsLock) {
- mRestrictionSettings.dumpLocked(pw, " " + prefix);
- }
+ mRestrictionSettings.dump(pw, " " + prefix);
mConstantsObserver.dump(pw, " " + prefix);
for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
pw.println();
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index 6bb5def26b9d..bbf586123e1c 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -56,19 +56,27 @@ public final class HostingRecord {
private final String mDefiningPackageName;
private final int mDefiningUid;
private final boolean mIsTopApp;
+ private final String mDefiningProcessName;
public HostingRecord(String hostingType) {
this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */,
- -1 /* mDefiningUid */, false /* isTopApp */);
+ -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */);
}
public HostingRecord(String hostingType, ComponentName hostingName) {
this(hostingType, hostingName, REGULAR_ZYGOTE);
}
+ public HostingRecord(String hostingType, ComponentName hostingName, String definingPackageName,
+ int definingUid, String definingProcessName) {
+ this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName,
+ definingUid, false /* isTopApp */, definingProcessName);
+ }
+
public HostingRecord(String hostingType, ComponentName hostingName, boolean isTopApp) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
- null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */);
+ null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
+ null /* definingProcessName */);
}
public HostingRecord(String hostingType, String hostingName) {
@@ -81,17 +89,19 @@ public final class HostingRecord {
private HostingRecord(String hostingType, String hostingName, int hostingZygote) {
this(hostingType, hostingName, hostingZygote, null /* definingPackageName */,
- -1 /* mDefiningUid */, false /* isTopApp */);
+ -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */);
}
private HostingRecord(String hostingType, String hostingName, int hostingZygote,
- String definingPackageName, int definingUid, boolean isTopApp) {
+ String definingPackageName, int definingUid, boolean isTopApp,
+ String definingProcessName) {
mHostingType = hostingType;
mHostingName = hostingName;
mHostingZygote = hostingZygote;
mDefiningPackageName = definingPackageName;
mDefiningUid = definingUid;
mIsTopApp = isTopApp;
+ mDefiningProcessName = definingProcessName;
}
public String getType() {
@@ -127,12 +137,24 @@ public final class HostingRecord {
}
/**
+ * Returns the processName of the component we want to start as specified in the defining app's
+ * manifest.
+ *
+ * @return the processName of the process in the hosting application
+ */
+ public String getDefiningProcessName() {
+ return mDefiningProcessName;
+ }
+
+ /**
* Creates a HostingRecord for a process that must spawn from the webview zygote
* @param hostingName name of the component to be hosted in this process
* @return The constructed HostingRecord
*/
- public static HostingRecord byWebviewZygote(ComponentName hostingName) {
- return new HostingRecord("", hostingName.toShortString(), WEBVIEW_ZYGOTE);
+ public static HostingRecord byWebviewZygote(ComponentName hostingName,
+ String definingPackageName, int definingUid, String definingProcessName) {
+ return new HostingRecord("", hostingName.toShortString(), WEBVIEW_ZYGOTE,
+ definingPackageName, definingUid, false /* isTopApp */, definingProcessName);
}
/**
@@ -143,9 +165,9 @@ public final class HostingRecord {
* @return The constructed HostingRecord
*/
public static HostingRecord byAppZygote(ComponentName hostingName, String definingPackageName,
- int definingUid) {
+ int definingUid, String definingProcessName) {
return new HostingRecord("", hostingName.toShortString(), APP_ZYGOTE,
- definingPackageName, definingUid, false /* isTopApp */);
+ definingPackageName, definingUid, false /* isTopApp */, definingProcessName);
}
/**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 763bbee66423..2c2e7c40c9c3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -70,7 +70,6 @@ import android.app.IApplicationThread;
import android.app.IProcessObserver;
import android.app.IUidObserver;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -363,59 +362,6 @@ public final class ProcessList {
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
/**
- * Native heap allocations will now have a non-zero tag in the most significant byte.
- * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
- * Pointers</a>
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
- private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
-
- /**
- * Native heap allocations in AppZygote process and its descendants will now have a
- * non-zero tag in the most significant byte.
- * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
- * Pointers</a>
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
- private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677;
-
- /**
- * Enable asynchronous (ASYNC) memory tag checking in this process. This
- * flag will only have an effect on hardware supporting the ARM Memory
- * Tagging Extension (MTE).
- */
- @ChangeId
- @Disabled
- private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id.
-
- /**
- * Enable synchronous (SYNC) memory tag checking in this process. This flag
- * will only have an effect on hardware supporting the ARM Memory Tagging
- * Extension (MTE). If both NATIVE_MEMTAG_ASYNC and this option is selected,
- * this option takes preference and MTE is enabled in SYNC mode.
- */
- @ChangeId
- @Disabled
- private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
-
- /**
- * Enable automatic zero-initialization of native heap memory allocations.
- */
- @ChangeId
- @Disabled
- private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id.
-
- /**
- * Enable sampled memory bug detection in the app.
- * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
- */
- @ChangeId
- @Disabled
- private static final long GWP_ASAN = 135634846; // This is a bug id.
-
- /**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
@@ -1681,136 +1627,6 @@ public final class ProcessList {
return gidArray;
}
- private int memtagModeToZygoteMemtagLevel(int memtagMode) {
- switch (memtagMode) {
- case ApplicationInfo.MEMTAG_ASYNC:
- return Zygote.MEMORY_TAG_LEVEL_ASYNC;
- case ApplicationInfo.MEMTAG_SYNC:
- return Zygote.MEMORY_TAG_LEVEL_SYNC;
- default:
- return Zygote.MEMORY_TAG_LEVEL_NONE;
- }
- }
-
- // Returns the requested memory tagging level.
- private int getRequestedMemtagLevel(ProcessRecord app) {
- // Look at the process attribute first.
- if (app.processInfo != null
- && app.processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
- return memtagModeToZygoteMemtagLevel(app.processInfo.memtagMode);
- }
-
- // Then at the application attribute.
- if (app.info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) {
- return memtagModeToZygoteMemtagLevel(app.info.getMemtagMode());
- }
-
- if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) {
- return Zygote.MEMORY_TAG_LEVEL_SYNC;
- }
-
- if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_ASYNC, app.info)) {
- return Zygote.MEMORY_TAG_LEVEL_ASYNC;
- }
-
- // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute.
- if (!app.info.allowsNativeHeapPointerTagging()) {
- return Zygote.MEMORY_TAG_LEVEL_NONE;
- }
-
- String defaultLevel = SystemProperties.get("persist.arm64.memtag.app_default");
- if ("sync".equals(defaultLevel)) {
- return Zygote.MEMORY_TAG_LEVEL_SYNC;
- } else if ("async".equals(defaultLevel)) {
- return Zygote.MEMORY_TAG_LEVEL_ASYNC;
- }
-
- // Check to see that the compat feature for TBI is enabled.
- if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
- return Zygote.MEMORY_TAG_LEVEL_TBI;
- }
-
- return Zygote.MEMORY_TAG_LEVEL_NONE;
- }
-
- private int decideTaggingLevel(ProcessRecord app) {
- // Get the desired tagging level (app manifest + compat features).
- int level = getRequestedMemtagLevel(app);
-
- // Take into account the hardware capabilities.
- if (Zygote.nativeSupportsMemoryTagging()) {
- // MTE devices can not do TBI, because the Zygote process already has live MTE
- // allocations. Downgrade TBI to NONE.
- if (level == Zygote.MEMORY_TAG_LEVEL_TBI) {
- level = Zygote.MEMORY_TAG_LEVEL_NONE;
- }
- } else if (Zygote.nativeSupportsTaggedPointers()) {
- // TBI-but-not-MTE devices downgrade MTE modes to TBI.
- // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with
- // the "fake" pointer tagging (TBI).
- if (level == Zygote.MEMORY_TAG_LEVEL_ASYNC || level == Zygote.MEMORY_TAG_LEVEL_SYNC) {
- level = Zygote.MEMORY_TAG_LEVEL_TBI;
- }
- } else {
- // Otherwise disable all tagging.
- level = Zygote.MEMORY_TAG_LEVEL_NONE;
- }
-
- return level;
- }
-
- private int decideTaggingLevelForAppZygote(ProcessRecord app) {
- int level = decideTaggingLevel(app);
- // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
- if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info)
- && level == Zygote.MEMORY_TAG_LEVEL_TBI) {
- level = Zygote.MEMORY_TAG_LEVEL_NONE;
- }
- return level;
- }
-
- private int decideGwpAsanLevel(ProcessRecord app) {
- // Look at the process attribute first.
- if (app.processInfo != null
- && app.processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) {
- return app.processInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_ALWAYS
- ? Zygote.GWP_ASAN_LEVEL_ALWAYS
- : Zygote.GWP_ASAN_LEVEL_NEVER;
- }
- // Then at the application attribute.
- if (app.info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) {
- return app.info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS
- ? Zygote.GWP_ASAN_LEVEL_ALWAYS
- : Zygote.GWP_ASAN_LEVEL_NEVER;
- }
- // If the app does not specify gwpAsanMode, the default behavior is lottery among the
- // system apps, and disabled for user apps, unless overwritten by the compat feature.
- if (mPlatformCompat.isChangeEnabled(GWP_ASAN, app.info)) {
- return Zygote.GWP_ASAN_LEVEL_ALWAYS;
- }
- if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- return Zygote.GWP_ASAN_LEVEL_LOTTERY;
- }
- return Zygote.GWP_ASAN_LEVEL_NEVER;
- }
-
- private boolean enableNativeHeapZeroInit(ProcessRecord app) {
- // Look at the process attribute first.
- if (app.processInfo != null
- && app.processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) {
- return app.processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED;
- }
- // Then at the application attribute.
- if (app.info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) {
- return app.info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED;
- }
- // Compat feature last.
- if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) {
- return true;
- }
- return false;
- }
-
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@@ -1992,8 +1808,6 @@ public final class ProcessList {
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
- runtimeFlags |= decideGwpAsanLevel(app);
-
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
@@ -2024,23 +1838,21 @@ public final class ProcessList {
app.setRequiredAbi(requiredAbi);
app.setInstructionSet(instructionSet);
- // If instructionSet is non-null, this indicates that the system_server is spawning a
- // process with an ISA that may be different from its own. System (kernel and hardware)
- // compatibility for these features is checked in the decideTaggingLevel in the
- // system_server process (not the child process). As both MTE and TBI are only supported
- // in aarch64, we can simply ensure that the new process is also aarch64. This prevents
- // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should
- // enable some tagging variant. Theoretically, a 32-bit system server could exist that
- // spawns 64-bit processes, in which case the new process won't get any tagging. This is
- // fine as we haven't seen this configuration in practice, and we can reasonable assume
- // that if tagging is desired, the system server will be 64-bit.
- if (instructionSet == null || instructionSet.equals("arm64")) {
- runtimeFlags |= decideTaggingLevel(app);
+ // If this was an external service, the package name and uid in the passed in
+ // ApplicationInfo have been changed to match those of the calling package;
+ // that will incorrectly apply compat feature overrides for the calling package instead
+ // of the defining one.
+ ApplicationInfo definingAppInfo;
+ if (hostingRecord.getDefiningPackageName() != null) {
+ definingAppInfo = new ApplicationInfo(app.info);
+ definingAppInfo.packageName = hostingRecord.getDefiningPackageName();
+ definingAppInfo.uid = uid;
+ } else {
+ definingAppInfo = app.info;
}
- if (enableNativeHeapZeroInit(app)) {
- runtimeFlags |= Zygote.NATIVE_HEAP_ZERO_INIT;
- }
+ runtimeFlags |= Zygote.getMemorySafetyRuntimeFlags(
+ definingAppInfo, app.processInfo, instructionSet, mPlatformCompat);
// the per-user SELinux context must be set
if (TextUtils.isEmpty(app.info.seInfoUser)) {
@@ -2299,8 +2111,7 @@ public final class ProcessList {
// not the calling one.
appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
appInfo.uid = uid;
- int runtimeFlags = decideTaggingLevelForAppZygote(app);
- appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags);
+ appZygote = new AppZygote(appInfo, app.processInfo, uid, firstUid, lastUid);
mAppZygotes.put(app.info.processName, uid, appZygote);
zygoteProcessList = new ArrayList<ProcessRecord>();
mAppZygoteProcesses.put(appZygote, zygoteProcessList);
@@ -2532,7 +2343,7 @@ public final class ProcessList {
ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
- boolean supplemental, int supplementalUid,
+ boolean isSdkSandbox, int sdkSandboxUid,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord app;
@@ -2626,8 +2437,8 @@ public final class ProcessList {
if (app == null) {
checkSlow(startTime, "startProcess: creating new process record");
- app = newProcessRecordLocked(info, processName, isolated, isolatedUid, supplemental,
- supplementalUid, hostingRecord);
+ app = newProcessRecordLocked(info, processName, isolated, isolatedUid, isSdkSandbox,
+ sdkSandboxUid, hostingRecord);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
@@ -3122,13 +2933,13 @@ public final class ProcessList {
@GuardedBy("mService")
ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
- boolean isolated, int isolatedUid, boolean supplemental, int supplementalUid,
+ boolean isolated, int isolatedUid, boolean isSdkSandbox, int sdkSandboxUid,
HostingRecord hostingRecord) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
- if (supplemental) {
- uid = supplementalUid;
+ if (isSdkSandbox) {
+ uid = sdkSandboxUid;
}
if (isolated) {
if (isolatedUid == 0) {
@@ -3158,7 +2969,8 @@ public final class ProcessList {
FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
- final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
+ final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
+ hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName());
final ProcessStateRecord state = r.mState;
if (!mService.mBooted && !mService.mBooting
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index be187e21db47..7672d10e27bd 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -492,24 +492,33 @@ class ProcessRecord implements WindowProcessListener {
ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
int _uid) {
+ this(_service, _info, _processName, _uid, -1, null);
+ }
+
+ ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
+ int _uid, int _definingUid, String _definingProcessName) {
mService = _service;
mProcLock = _service.mProcLock;
info = _info;
ProcessInfo procInfo = null;
if (_service.mPackageManagerInt != null) {
- ArrayMap<String, ProcessInfo> processes =
- _service.mPackageManagerInt.getProcessesForUid(_uid);
- if (processes != null) {
- procInfo = processes.get(_processName);
- if (procInfo != null && procInfo.deniedPermissions == null
- && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
- && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
- && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) {
- // If this process hasn't asked for permissions to be denied, or for a
- // non-default GwpAsan mode, or any other non-default setting, then we don't
- // care about it.
- procInfo = null;
- }
+ if (_definingUid > 0) {
+ ArrayMap<String, ProcessInfo> processes =
+ _service.mPackageManagerInt.getProcessesForUid(_definingUid);
+ if (processes != null) procInfo = processes.get(_definingProcessName);
+ } else {
+ ArrayMap<String, ProcessInfo> processes =
+ _service.mPackageManagerInt.getProcessesForUid(_uid);
+ if (processes != null) procInfo = processes.get(_processName);
+ }
+ if (procInfo != null && procInfo.deniedPermissions == null
+ && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
+ && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
+ && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) {
+ // If this process hasn't asked for permissions to be denied, or for a
+ // non-default GwpAsan mode, or any other non-default setting, then we don't
+ // care about it.
+ procInfo = null;
}
}
processInfo = procInfo;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index bf2876f4a755..c53d4d6b5015 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -94,8 +94,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
final long createRealTime; // when this service was created
- final boolean supplemental; // whether this is a supplemental service
- final int supplementedAppUid; // the app uid for which this supplemental service is running
+ final boolean isSdkSandbox; // whether this is a sdk sandbox service
+ final int sdkSandboxClientAppUid; // the app uid for which this sdk sandbox service is running
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
@@ -105,7 +105,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
ProcessRecord app; // where this service is running or null.
ProcessRecord isolationHostProc; // process which we've started for this service (used for
- // isolated and supplemental processes)
+ // isolated and sdk sandbox processes)
ServiceState tracker; // tracking service execution, may be null
ServiceState restartTracker; // tracking service restart
boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
@@ -579,7 +579,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
ServiceRecord(ActivityManagerService ams, ComponentName name,
ComponentName instanceName, String definingPackageName, int definingUid,
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
- Runnable restarter, String supplementalProcessName, int supplementedAppUid) {
+ Runnable restarter, String sdkSandboxProcessName, int sdkSandboxClientAppUid) {
this.ams = ams;
this.name = name;
this.instanceName = instanceName;
@@ -590,12 +590,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
- supplemental = supplementalProcessName != null;
- this.supplementedAppUid = supplementedAppUid;
+ isSdkSandbox = sdkSandboxProcessName != null;
+ this.sdkSandboxClientAppUid = sdkSandboxClientAppUid;
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
processName = sInfo.processName + ":" + instanceName.getClassName();
- } else if (supplementalProcessName != null) {
- processName = supplementalProcessName;
+ } else if (sdkSandboxProcessName != null) {
+ processName = sdkSandboxProcessName;
} else {
processName = sInfo.processName;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b6801fbdd825..1095cf0a0d30 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2613,7 +2613,7 @@ class UserController implements Handler.Callback {
if (getStartedUserState(userId) == null) {
return false;
}
- if (!getUserInfo(userId).isManagedProfile()) {
+ if (!mInjector.getUserManager().isCredentialSharedWithParent(userId)) {
return false;
}
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 0edbea0dbd28..3efd8ad0581b 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -1320,15 +1320,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
// Make sure after resetting the game mode is still supported.
// If not, set the game mode to standard
int gameMode = getGameMode(packageName, userId);
- int newGameMode = gameMode;
GamePackageConfiguration config = null;
synchronized (mOverrideConfigLock) {
config = mOverrideConfigs.get(packageName);
}
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
+ if (config == null) {
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
}
+ final int newGameMode = getNewGameMode(gameMode, config);
+ if (gameMode != newGameMode) {
+ setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
+ return;
+ }
+ setGameMode(packageName, gameMode, userId);
+ }
+
+ private int getNewGameMode(int gameMode, GamePackageConfiguration config) {
+ int newGameMode = gameMode;
if (config != null) {
int modesBitfield = config.getAvailableGameModesBitfield();
// Remove UNSUPPORTED to simplify the logic here, since we really just
@@ -1350,11 +1361,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// UNSUPPORTED, then set to UNSUPPORTED
newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
}
- if (gameMode != newGameMode) {
- setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
- return;
- }
- setGameMode(packageName, gameMode, userId);
+ return newGameMode;
}
/**
@@ -1412,7 +1419,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
for (final String packageName : packageNames) {
int gameMode = getGameMode(packageName, userId);
- int newGameMode = gameMode;
// Make sure the user settings and package configs don't conflict.
// I.e. the user setting is set to a mode that no longer available due to
// config/manifest changes.
@@ -1421,27 +1427,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
synchronized (mDeviceConfigLock) {
config = mConfigs.get(packageName);
}
- if (config != null) {
- int modesBitfield = config.getAvailableGameModesBitfield();
- // Remove UNSUPPORTED to simplify the logic here, since we really just
- // want to check if we support selectable game modes
- modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
- if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
- if (bitFieldContainsModeBitmask(modesBitfield,
- GameManager.GAME_MODE_STANDARD)) {
- // If the current set mode isn't supported,
- // but we support STANDARD, then set the mode to STANDARD.
- newGameMode = GameManager.GAME_MODE_STANDARD;
- } else {
- // If we don't support any game modes, then set to UNSUPPORTED
- newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
- }
- }
- } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
- // If we have no config for the package, but the configured mode is not
- // UNSUPPORTED, then set to UNSUPPORTED
- newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
- }
+ final int newGameMode = getNewGameMode(gameMode, config);
if (newGameMode != gameMode) {
setGameMode(packageName, newGameMode, userId);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e2d00f75c86e..ef7c93d6502f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -113,7 +113,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
@@ -178,6 +177,7 @@ import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemServiceManager;
import com.android.server.pm.PackageList;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.component.ParsedAttribution;
import dalvik.annotation.optimization.NeverCompile;
@@ -4551,15 +4551,16 @@ public class AppOpsService extends IAppOpsService.Stub {
return new PackageVerificationResult(null,
/* isAttributionTagValid */ true);
}
- if (Process.isSupplemental(uid)) {
- // Supplemental processes run in their own UID range, but their associated
- // UID for checks should always be the UID of the supplemental package.
+ if (Process.isSdkSandboxUid(uid)) {
+ // SDK sandbox processes run in their own UID range, but their associated
+ // UID for checks should always be the UID of the package implementing SDK sandbox
+ // service.
// TODO: We will need to modify the callers of this function instead, so
// modifications and checks against the app ops state are done with the
// correct UID.
try {
final PackageManager pm = mContext.getPackageManager();
- final String supplementalPackageName = pm.getSupplementalProcessPackageName();
+ final String supplementalPackageName = pm.getSdkSandboxPackageName();
if (Objects.equals(packageName, supplementalPackageName)) {
int supplementalAppId = pm.getPackageUid(supplementalPackageName,
PackageManager.PackageInfoFlags.of(0));
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index daf3561d75ce..270a61b32390 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -396,7 +396,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
AudioSystem.STREAM_VOICE_CALL);
- List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
+ List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(
+ attr, false /* forVolume */);
if (devices.isEmpty()) {
if (mAudioService.isPlatformVoice()) {
Log.w(TAG,
@@ -1564,6 +1565,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
private AtomicBoolean mMusicMuted = new AtomicBoolean(false);
+ private static <T> boolean hasIntersection(Set<T> a, Set<T> b) {
+ for (T e : a) {
+ if (b.contains(e)) return true;
+ }
+ return false;
+ }
+
boolean messageMutesMusic(int message) {
if (message == 0) {
return false;
@@ -1573,8 +1581,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|| message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT
|| message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE)
&& AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
- && mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(
- mAudioService.getDevicesForStream(AudioSystem.STREAM_MUSIC))) {
+ && hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
+ mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0b9fb1a72f8d..82476d8d1df8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -195,6 +195,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -1808,6 +1809,10 @@ public class AudioService extends IAudioService.Stub
* @param caller caller of this method
*/
private void updateVolumeStates(int device, int streamType, String caller) {
+ // Handle device volume aliasing of SPEAKER_SAFE.
+ if (device == AudioSystem.DEVICE_OUT_SPEAKER_SAFE) {
+ device = AudioSystem.DEVICE_OUT_SPEAKER;
+ }
if (!mStreamStates[streamType].hasIndexForDevice(device)) {
// set the default value, if device is affected by a full/fix/abs volume rule, it
// will taken into account in checkFixedVolumeDevices()
@@ -1818,8 +1823,10 @@ public class AudioService extends IAudioService.Stub
}
// Check if device to be updated is routed for the given audio stream
+ // This may include devices such as SPEAKER_SAFE.
List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt(
- new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+ new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build(),
+ true /* forVolume */);
for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
device)) {
@@ -2687,7 +2694,7 @@ public class AudioService extends IAudioService.Stub
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
enforceQueryStateOrModifyRoutingPermission();
- return getDevicesForAttributesInt(attributes);
+ return getDevicesForAttributesInt(attributes, false /* forVolume */);
}
/** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
@@ -2697,7 +2704,7 @@ public class AudioService extends IAudioService.Stub
*/
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
@NonNull AudioAttributes attributes) {
- return getDevicesForAttributesInt(attributes);
+ return getDevicesForAttributesInt(attributes, false /* forVolume */);
}
/**
@@ -2719,9 +2726,9 @@ public class AudioService extends IAudioService.Stub
}
protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
Objects.requireNonNull(attributes);
- return mAudioSystem.getDevicesForAttributes(attributes);
+ return mAudioSystem.getDevicesForAttributes(attributes, forVolume);
}
/** Indicates no special treatment in the handling of the volume adjustement */
@@ -3688,8 +3695,7 @@ public class AudioService extends IAudioService.Stub
int streamType = getBluetoothContextualVolumeStream(newMode);
- final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
- mAudioSystem.getDevicesForStream(streamType));
+ final Set<Integer> deviceTypes = getDeviceSetForStreamDirect(streamType);
final Set<Integer> absVolumeMultiModeCaseDevices = AudioSystem.intersectionAudioDeviceTypes(
mAbsVolumeMultiModeCaseDevices, deviceTypes);
if (absVolumeMultiModeCaseDevices.isEmpty()) {
@@ -6253,55 +6259,107 @@ public class AudioService extends IAudioService.Stub
}
}
- /** only public for mocking/spying, do not call outside of AudioService */
+ /**
+ * Returns device associated with the stream volume.
+ *
+ * Only public for mocking/spying, do not call outside of AudioService.
+ * Device volume aliasing means DEVICE_OUT_SPEAKER may be returned for
+ * DEVICE_OUT_SPEAKER_SAFE.
+ */
@VisibleForTesting
public int getDeviceForStream(int stream) {
- int device = getDevicesForStreamInt(stream);
- if ((device & (device - 1)) != 0) {
+ return selectOneAudioDevice(getDeviceSetForStream(stream));
+ }
+
+ /*
+ * Must match native apm_extract_one_audio_device() used in getDeviceForVolume()
+ * or the wrong device volume may be adjusted.
+ */
+ private int selectOneAudioDevice(Set<Integer> deviceSet) {
+ if (deviceSet.isEmpty()) {
+ return AudioSystem.DEVICE_NONE;
+ } else if (deviceSet.size() == 1) {
+ return deviceSet.iterator().next();
+ } else {
// Multiple device selection is either:
// - speaker + one other device: give priority to speaker in this case.
// - one A2DP device + another device: happens with duplicated output. In this case
// retain the device on the A2DP output as the other must not correspond to an active
// selection if not the speaker.
// - HDMI-CEC system audio mode only output: give priority to available item in order.
- // FIXME: Haven't applied audio device type refactor to this API
- // as it is going to be deprecated.
- if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
- device = AudioSystem.DEVICE_OUT_SPEAKER;
- } else if ((device & AudioSystem.DEVICE_OUT_HDMI_ARC) != 0) {
- // FIXME(b/184944421): DEVICE_OUT_HDMI_EARC has two bits set,
- // so it must be handled correctly as it aliases
- // with DEVICE_OUT_HDMI_ARC | DEVICE_OUT_EARPIECE.
- device = AudioSystem.DEVICE_OUT_HDMI_ARC;
- } else if ((device & AudioSystem.DEVICE_OUT_SPDIF) != 0) {
- device = AudioSystem.DEVICE_OUT_SPDIF;
- } else if ((device & AudioSystem.DEVICE_OUT_AUX_LINE) != 0) {
- device = AudioSystem.DEVICE_OUT_AUX_LINE;
+
+ if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
+ return AudioSystem.DEVICE_OUT_SPEAKER;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER_SAFE)) {
+ // Note: DEVICE_OUT_SPEAKER_SAFE not present in getDeviceSetForStreamDirect
+ return AudioSystem.DEVICE_OUT_SPEAKER_SAFE;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_ARC)) {
+ return AudioSystem.DEVICE_OUT_HDMI_ARC;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_EARC)) {
+ return AudioSystem.DEVICE_OUT_HDMI_EARC;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_AUX_LINE)) {
+ return AudioSystem.DEVICE_OUT_AUX_LINE;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPDIF)) {
+ return AudioSystem.DEVICE_OUT_SPDIF;
} else {
- for (int deviceType : AudioSystem.DEVICE_OUT_ALL_A2DP_SET) {
- if ((deviceType & device) == deviceType) {
+ // At this point, deviceSet should contain exactly one A2DP device;
+ // regardless, return the first A2DP device in numeric order.
+ // If there is no A2DP device, this falls through to log an error.
+ for (int deviceType : deviceSet) {
+ if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(deviceType)) {
return deviceType;
}
}
}
}
- return device;
+ Log.w(TAG, "selectOneAudioDevice returning DEVICE_NONE from invalid device combination "
+ + AudioSystem.deviceSetToString(deviceSet));
+ return AudioSystem.DEVICE_NONE;
}
/**
* @see AudioManager#getDevicesForStream(int)
+ * @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
+ * will have multi-bit device types since S.
+ * Use {@link #getDevicesForAttributes()} instead.
*/
- public int getDevicesForStream(int streamType) {
+ @Override
+ @Deprecated
+ public int getDeviceMaskForStream(int streamType) {
ensureValidStreamType(streamType);
+ // no permission required
final long token = Binder.clearCallingIdentity();
try {
- return mAudioSystem.getDevicesForStream(streamType);
+ return AudioSystem.getDeviceMaskFromSet(
+ getDeviceSetForStreamDirect(streamType));
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private int getDevicesForStreamInt(int stream) {
+ /**
+ * Returns the devices associated with a stream type.
+ *
+ * SPEAKER_SAFE will alias to SPEAKER.
+ */
+ @NonNull
+ private Set<Integer> getDeviceSetForStreamDirect(int stream) {
+ final AudioAttributes attr =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
+ Set<Integer> deviceSet =
+ AudioSystem.generateAudioDeviceTypesSet(
+ getDevicesForAttributesInt(attr, true /* forVolume */));
+ return deviceSet;
+ }
+
+ /**
+ * Returns a reference to the list of devices for the stream, do not modify.
+ *
+ * The device returned may be aliased to the actual device whose volume curve
+ * will be used. For example DEVICE_OUT_SPEAKER_SAFE aliases to DEVICE_OUT_SPEAKER.
+ */
+ @NonNull
+ public Set<Integer> getDeviceSetForStream(int stream) {
ensureValidStreamType(stream);
synchronized (VolumeStreamState.class) {
return mStreamStates[stream].observeDevicesForStream_syncVSS(true);
@@ -6313,11 +6371,10 @@ public class AudioService extends IAudioService.Stub
synchronized (VolumeStreamState.class) {
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (stream != skipStream) {
- int devices = mStreamStates[stream].observeDevicesForStream_syncVSS(
- false /*checkOthers*/);
-
- Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices);
- for (Integer device : devicesSet) {
+ Set<Integer> deviceSet =
+ mStreamStates[stream].observeDevicesForStream_syncVSS(
+ false /*checkOthers*/);
+ for (Integer device : deviceSet) {
// Update volume states for devices routed for the stream
updateVolumeStates(device, stream,
"AudioService#onObserveDevicesForAllStreams");
@@ -6490,7 +6547,8 @@ public class AudioService extends IAudioService.Stub
.setUsage(AudioAttributes.USAGE_MEDIA)
.build();
// calling getDevice*Int to bypass permission check
- final List<AudioDeviceAttributes> devices = getDevicesForAttributesInt(attributes);
+ final List<AudioDeviceAttributes> devices =
+ getDevicesForAttributesInt(attributes, true /* forVolume */);
for (AudioDeviceAttributes device : devices) {
if (getDeviceVolumeBehaviorInt(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED) {
return true;
@@ -6637,7 +6695,7 @@ public class AudioService extends IAudioService.Stub
&& DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
- && (newDevice & mAudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
+ && getDeviceSetForStreamDirect(AudioSystem.STREAM_MUSIC).contains(newDevice)) {
if (DEBUG_VOL) {
Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
@@ -7025,7 +7083,7 @@ public class AudioService extends IAudioService.Stub
private boolean mIsMuted;
private boolean mIsMutedInternally;
private String mVolumeIndexSettingName;
- private int mObservedDevices;
+ @NonNull private Set<Integer> mObservedDeviceSet = new TreeSet<>();
private final SparseIntArray mIndexMap = new SparseIntArray(8) {
@Override
@@ -7092,17 +7150,30 @@ public class AudioService extends IAudioService.Stub
}
}
+ /**
+ * Returns a list of devices associated with the stream type.
+ *
+ * This is a reference to the local list, do not modify.
+ */
@GuardedBy("VolumeStreamState.class")
- public int observeDevicesForStream_syncVSS(boolean checkOthers) {
+ @NonNull
+ public Set<Integer> observeDevicesForStream_syncVSS(
+ boolean checkOthers) {
if (!mSystemServer.isPrivileged()) {
- return AudioSystem.DEVICE_NONE;
+ return new TreeSet<Integer>();
}
- final int devices = mAudioSystem.getDevicesForStream(mStreamType);
- if (devices == mObservedDevices) {
- return devices;
+ final Set<Integer> deviceSet =
+ getDeviceSetForStreamDirect(mStreamType);
+ if (deviceSet.equals(mObservedDeviceSet)) {
+ return mObservedDeviceSet;
}
- final int prevDevices = mObservedDevices;
- mObservedDevices = devices;
+
+ // Use legacy bit masks for message signalling.
+ // TODO(b/185386781): message needs update since it uses devices bit-mask.
+ final int devices = AudioSystem.getDeviceMaskFromSet(deviceSet);
+ final int prevDevices = AudioSystem.getDeviceMaskFromSet(mObservedDeviceSet);
+
+ mObservedDeviceSet = deviceSet;
if (checkOthers) {
// one stream's devices have changed, check the others
postObserveDevicesForAllStreams(mStreamType);
@@ -7118,7 +7189,7 @@ public class AudioService extends IAudioService.Stub
SENDMSG_QUEUE, prevDevices /*arg1*/, devices /*arg2*/,
// ok to send reference to this object, it is final
mStreamDevicesChanged /*obj*/, 0 /*delay*/);
- return devices;
+ return mObservedDeviceSet;
}
public @Nullable String getSettingNameForDevice(int device) {
@@ -7549,19 +7620,7 @@ public class AudioService extends IAudioService.Stub
}
pw.println();
pw.print(" Devices: ");
- final int devices = getDevicesForStreamInt(mStreamType);
- int device, i = 0, n = 0;
- // iterate all devices from 1 to DEVICE_OUT_DEFAULT exclusive
- // (the default device is not returned by getDevicesForStreamInt)
- while ((device = 1 << i) != AudioSystem.DEVICE_OUT_DEFAULT) {
- if ((devices & device) != 0) {
- if (n++ > 0) {
- pw.print(", ");
- }
- pw.print(AudioSystem.getOutputDeviceName(device));
- }
- i++;
- }
+ pw.print(AudioSystem.deviceSetToString(getDeviceSetForStream(mStreamType)));
}
}
@@ -9318,7 +9377,9 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.setForceUse_Async(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config,
"setHdmiSystemAudioSupported");
}
- device = getDevicesForStreamInt(AudioSystem.STREAM_MUSIC);
+ // TODO(b/185386781): Update AudioManager API to use device list.
+ // So far, this value appears to be advisory for debug log.
+ device = getDeviceMaskForStream(AudioSystem.STREAM_MUSIC);
}
}
return device;
@@ -11273,6 +11334,7 @@ public class AudioService extends IAudioService.Stub
@Override
public void setActiveAssistantServiceUids(int [] activeAssistantUids) {
enforceModifyAudioRoutingPermission();
+ Objects.requireNonNull(activeAssistantUids);
synchronized (mSettingsLock) {
mActiveAssistantServiceUids = activeAssistantUids;
}
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a70b4701bb7b..1429b3cf2041 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -24,12 +24,14 @@ import android.media.AudioSystem;
import android.media.audiopolicy.AudioMix;
import android.os.SystemClock;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -50,16 +52,14 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
* in measured methods
*/
private static final boolean ENABLE_GETDEVICES_STATS = false;
- private static final int NB_MEASUREMENTS = 2;
- private static final int METHOD_GETDEVICESFORSTREAM = 0;
- private static final int METHOD_GETDEVICESFORATTRIBUTES = 1;
+ private static final int NB_MEASUREMENTS = 1;
+ private static final int METHOD_GETDEVICESFORATTRIBUTES = 0;
private long[] mMethodTimeNs;
private int[] mMethodCallCounter;
- private String[] mMethodNames = {"getDevicesForStream", "getDevicesForAttributes"};
+ private String[] mMethodNames = {"getDevicesForAttributes"};
private static final boolean USE_CACHE_FOR_GETDEVICES = true;
- private ConcurrentHashMap<Integer, Integer> mDevicesForStreamCache;
- private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>>
+ private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
private static final Object sRoutingListenerLock = new Object();
@@ -111,8 +111,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
sSingletonDefaultAdapter = new AudioSystemAdapter();
AudioSystem.setRoutingCallback(sSingletonDefaultAdapter);
if (USE_CACHE_FOR_GETDEVICES) {
- sSingletonDefaultAdapter.mDevicesForStreamCache =
- new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
sSingletonDefaultAdapter.mDevicesForAttrCache =
new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS];
@@ -129,11 +127,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
if (DEBUG_CACHE) {
Log.d(TAG, "---- clearing cache ----------");
}
- if (mDevicesForStreamCache != null) {
- synchronized (mDevicesForStreamCache) {
- mDevicesForStreamCache.clear();
- }
- }
if (mDevicesForAttrCache != null) {
synchronized (mDevicesForAttrCache) {
mDevicesForAttrCache.clear();
@@ -142,85 +135,33 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
}
/**
- * Same as {@link AudioSystem#getDevicesForStream(int)}
- * @param stream a valid stream type
- * @return a mask of device types
- */
- public int getDevicesForStream(int stream) {
- if (!ENABLE_GETDEVICES_STATS) {
- return getDevicesForStreamImpl(stream);
- }
- mMethodCallCounter[METHOD_GETDEVICESFORSTREAM]++;
- final long startTime = SystemClock.uptimeNanos();
- final int res = getDevicesForStreamImpl(stream);
- mMethodTimeNs[METHOD_GETDEVICESFORSTREAM] += SystemClock.uptimeNanos() - startTime;
- return res;
- }
-
- private int getDevicesForStreamImpl(int stream) {
- if (USE_CACHE_FOR_GETDEVICES) {
- Integer res;
- synchronized (mDevicesForStreamCache) {
- res = mDevicesForStreamCache.get(stream);
- if (res == null) {
- res = AudioSystem.getDevicesForStream(stream);
- mDevicesForStreamCache.put(stream, res);
- if (DEBUG_CACHE) {
- Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
- + streamDeviceToDebugString(stream, res));
- }
- return res;
- }
- // cache hit
- mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]++;
- if (DEBUG_CACHE) {
- final int real = AudioSystem.getDevicesForStream(stream);
- if (res == real) {
- Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
- + streamDeviceToDebugString(stream, res) + " CACHE");
- } else {
- Log.e(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
- + streamDeviceToDebugString(stream, res)
- + " CACHE ERROR real dev=0x" + Integer.toHexString(real));
- }
- }
- }
- return res;
- }
- // not using cache
- return AudioSystem.getDevicesForStream(stream);
- }
-
- private static String streamDeviceToDebugString(int stream, int dev) {
- return " stream=" + stream + " dev=0x" + Integer.toHexString(dev);
- }
-
- /**
* Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)}
* @param attributes the attributes for which the routing is queried
* @return the devices that the stream with the given attributes would be routed to
*/
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
if (!ENABLE_GETDEVICES_STATS) {
- return getDevicesForAttributesImpl(attributes);
+ return getDevicesForAttributesImpl(attributes, forVolume);
}
mMethodCallCounter[METHOD_GETDEVICESFORATTRIBUTES]++;
final long startTime = SystemClock.uptimeNanos();
- final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(attributes);
+ final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(
+ attributes, forVolume);
mMethodTimeNs[METHOD_GETDEVICESFORATTRIBUTES] += SystemClock.uptimeNanos() - startTime;
return res;
}
private @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesImpl(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
if (USE_CACHE_FOR_GETDEVICES) {
ArrayList<AudioDeviceAttributes> res;
+ final Pair<AudioAttributes, Boolean> key = new Pair(attributes, forVolume);
synchronized (mDevicesForAttrCache) {
- res = mDevicesForAttrCache.get(attributes);
+ res = mDevicesForAttrCache.get(key);
if (res == null) {
- res = AudioSystem.getDevicesForAttributes(attributes);
- mDevicesForAttrCache.put(attributes, res);
+ res = AudioSystem.getDevicesForAttributes(attributes, forVolume);
+ mDevicesForAttrCache.put(key, res);
if (DEBUG_CACHE) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES]
+ attrDeviceToDebugString(attributes, res));
@@ -231,7 +172,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES]++;
if (DEBUG_CACHE) {
final ArrayList<AudioDeviceAttributes> real =
- AudioSystem.getDevicesForAttributes(attributes);
+ AudioSystem.getDevicesForAttributes(attributes, forVolume);
if (res.equals(real)) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES]
+ attrDeviceToDebugString(attributes, res) + " CACHE");
@@ -245,16 +186,13 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
return res;
}
// not using cache
- return AudioSystem.getDevicesForAttributes(attributes);
+ return AudioSystem.getDevicesForAttributes(attributes, forVolume);
}
private static String attrDeviceToDebugString(@NonNull AudioAttributes attr,
- @NonNull ArrayList<AudioDeviceAttributes> devices) {
- String ds = " attrUsage=" + attr.getSystemUsage();
- for (AudioDeviceAttributes ada : devices) {
- ds = ds.concat(" dev=0x" + Integer.toHexString(ada.getInternalType()));
- }
- return ds;
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return " attrUsage=" + attr.getSystemUsage() + " "
+ + AudioSystem.deviceSetToString(AudioSystem.generateAudioDeviceTypesSet(devices));
}
/**
@@ -514,19 +452,23 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
*/
public void dump(PrintWriter pw) {
pw.println("\nAudioSystemAdapter:");
- pw.println(" mDevicesForStreamCache:");
- if (mDevicesForStreamCache != null) {
- for (Integer stream : mDevicesForStreamCache.keySet()) {
- pw.println("\t stream:" + stream + " device:"
- + AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream)));
- }
- }
pw.println(" mDevicesForAttrCache:");
if (mDevicesForAttrCache != null) {
- for (AudioAttributes attr : mDevicesForAttrCache.keySet()) {
- pw.println("\t" + attr);
- for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) {
- pw.println("\t\t" + devAttr);
+ for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
+ entry : mDevicesForAttrCache.entrySet()) {
+ final AudioAttributes attributes = entry.getKey().first;
+ try {
+ final int stream = attributes.getVolumeControlStream();
+ pw.println("\t" + attributes + " forVolume: " + entry.getKey().second
+ + " stream: "
+ + AudioSystem.STREAM_NAMES[stream] + "(" + stream + ")");
+ for (AudioDeviceAttributes devAttr : entry.getValue()) {
+ pw.println("\t\t" + devAttr);
+ }
+ } catch (IllegalArgumentException e) {
+ // dump could fail if attributes do not map to a stream.
+ pw.println("\t dump failed for attributes: " + attributes);
+ Log.e(TAG, "dump failed", e);
}
}
}
@@ -540,7 +482,8 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
+ ": counter=" + mMethodCallCounter[i]
+ " time(ms)=" + (mMethodTimeNs[i] / 1E6)
+ (USE_CACHE_FOR_GETDEVICES
- ? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]) : ""));
+ ? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES])
+ : ""));
}
pw.println("\n");
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 63b27d82dab9..193cc5f679af 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -264,7 +264,8 @@ public class SpatializerHelper {
return;
}
mState = STATE_DISABLED_UNAVAILABLE;
- mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ mASA.getDevicesForAttributes(
+ DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
// note at this point mSpat is still not instantiated
}
@@ -298,7 +299,8 @@ public class SpatializerHelper {
case STATE_DISABLED_AVAILABLE:
break;
}
- mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ mASA.getDevicesForAttributes(
+ DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
// is media routed to a new device?
if (isWireless(ROUTING_DEVICES[0].getType())) {
@@ -865,7 +867,8 @@ public class SpatializerHelper {
}
AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
// going through adapter to take advantage of routing cache
- mASA.getDevicesForAttributes(attributes).toArray(devices);
+ mASA.getDevicesForAttributes(
+ attributes, false /* forVolume */).toArray(devices);
final boolean able = canBeSpatializedOnDevice(attributes, format, devices);
logd("canBeSpatialized returning " + able);
return able;
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index 5a6c6a51bd97..f3a73f043c29 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -8,6 +8,9 @@
},
{
"include-filter": "android.media.audio.cts.AudioFocusTest"
+ },
+ {
+ "include-filter": "android.media.audio.cts.SpatializerTest"
}
]
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 79705a32c264..bf69284df2f6 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -20,12 +20,8 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
-import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE;
-import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_FACE_SCANNING;
-import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_FP_SCANNING;
-import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_SWITCHING;
-import static com.android.server.biometrics.BiometricServiceStateProto.MULTI_SENSOR_STATE_UNKNOWN;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
@@ -100,14 +96,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
@Retention(RetentionPolicy.SOURCE)
@interface SessionState {}
- /** Defined in biometrics.proto */
- @IntDef({
- MULTI_SENSOR_STATE_UNKNOWN,
- MULTI_SENSOR_STATE_FACE_SCANNING,
- MULTI_SENSOR_STATE_FP_SCANNING})
- @Retention(RetentionPolicy.SOURCE)
- @interface MultiSensorState {}
-
/**
* Notify the holder of the AuthSession that the caller/client's binder has died. The
* holder (BiometricService) should schedule {@link AuthSession#onClientDied()} to be run
@@ -119,7 +107,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
private final Context mContext;
private final IStatusBarService mStatusBarService;
- private final IBiometricSysuiReceiver mSysuiReceiver;
+ @VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver;
private final KeyStore mKeyStore;
private final Random mRandom;
private final ClientDeathReceiver mClientDeathReceiver;
@@ -133,7 +121,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
private final long mRequestId;
private final long mOperationId;
private final int mUserId;
- private final IBiometricSensorReceiver mSensorReceiver;
+ @VisibleForTesting final IBiometricSensorReceiver mSensorReceiver;
// Original receiver from BiometricPrompt.
private final IBiometricServiceReceiver mClientReceiver;
private final String mOpPackageName;
@@ -143,10 +131,10 @@ public final class AuthSession implements IBinder.DeathRecipient {
// The current state, which can be either idle, called, or started
private @SessionState int mState = STATE_AUTH_IDLE;
private @BiometricMultiSensorMode int mMultiSensorMode;
- private @MultiSensorState int mMultiSensorState;
private int[] mSensors;
// TODO(b/197265902): merge into state
private boolean mCancelled;
+ private int mAuthenticatedSensorId = -1;
// For explicit confirmation, do not send to keystore until the user has confirmed
// the authentication.
private byte[] mTokenEscrow;
@@ -232,8 +220,16 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
}
- private void setSensorsToStateWaitingForCookie() throws RemoteException {
+ private void setSensorsToStateWaitingForCookie(boolean isTryAgain) throws RemoteException {
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
+ @BiometricSensor.SensorState final int state = sensor.getSensorState();
+ if (isTryAgain
+ && state != BiometricSensor.STATE_STOPPED
+ && state != BiometricSensor.STATE_CANCELING) {
+ Slog.d(TAG, "Skip retry because sensor: " + sensor.id + " is: " + state);
+ continue;
+ }
+
final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
final boolean requireConfirmation = isConfirmationRequired(sensor);
@@ -254,7 +250,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mSensors = new int[0];
mMultiSensorMode = BIOMETRIC_MULTI_SENSOR_DEFAULT;
- mMultiSensorState = MULTI_SENSOR_STATE_UNKNOWN;
mStatusBarService.showAuthenticationDialog(
mPromptInfo,
@@ -269,7 +264,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
mMultiSensorMode);
} else if (!mPreAuthInfo.eligibleSensors.isEmpty()) {
// Some combination of biometric or biometric|credential is requested
- setSensorsToStateWaitingForCookie();
+ setSensorsToStateWaitingForCookie(false /* isTryAgain */);
mState = STATE_AUTH_CALLED;
} else {
// No authenticators requested. This should never happen - an exception should have
@@ -283,6 +278,10 @@ public final class AuthSession implements IBinder.DeathRecipient {
Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie);
return;
}
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onCookieReceived after successful auth");
+ return;
+ }
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
sensor.goToStateCookieReturnedIfCookieMatches(cookie);
@@ -307,7 +306,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
mMultiSensorMode = getMultiSensorModeForNewSession(
mPreAuthInfo.eligibleSensors);
- mMultiSensorState = MULTI_SENSOR_STATE_UNKNOWN;
mStatusBarService.showAuthenticationDialog(mPromptInfo,
mSysuiReceiver,
@@ -381,9 +379,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
// sending the final error callback to the application.
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
try {
- final boolean shouldCancel = filter.apply(sensor);
- Slog.d(TAG, "sensorId: " + sensor.id + ", shouldCancel: " + shouldCancel);
- if (shouldCancel) {
+ if (filter.apply(sensor)) {
+ Slog.d(TAG, "Cancelling sensorId: " + sensor.id);
sensor.goToStateCancelling(mToken, mOpPackageName, mRequestId);
}
} catch (RemoteException e) {
@@ -412,10 +409,16 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
}
+ // do not propagate the error and let onAuthenticationSucceeded handle the new state
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onErrorReceived after successful auth (ignoring)");
+ return false;
+ }
+
mErrorEscrow = error;
mVendorCodeEscrow = vendorCode;
- final @BiometricAuthenticator.Modality int modality = sensorIdToModality(sensorId);
+ @Modality final int modality = sensorIdToModality(sensorId);
switch (mState) {
case STATE_AUTH_CALLED: {
@@ -430,7 +433,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
mState = STATE_SHOWING_DEVICE_CREDENTIAL;
mMultiSensorMode = BIOMETRIC_MULTI_SENSOR_DEFAULT;
- mMultiSensorState = MULTI_SENSOR_STATE_UNKNOWN;
mSensors = new int[0];
mStatusBarService.showAuthenticationDialog(
@@ -468,12 +470,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
return true;
} else {
mState = STATE_ERROR_PENDING_SYSUI;
- if (mMultiSensorMode == BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT
- && mMultiSensorState == MULTI_SENSOR_STATE_FACE_SCANNING) {
- // wait for the UI to signal when modality should switch
- Slog.d(TAG, "onErrorReceived: waiting for modality switch callback");
- mMultiSensorState = MULTI_SENSOR_STATE_SWITCHING;
- }
mStatusBarService.onBiometricError(modality, error, vendorCode);
}
break;
@@ -505,6 +501,11 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onAcquired(int sensorId, int acquiredInfo, int vendorCode) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onAcquired after successful auth");
+ return;
+ }
+
final String message = getAcquiredMessageForSensor(sensorId, acquiredInfo, vendorCode);
Slog.d(TAG, "sensorId: " + sensorId + " acquiredInfo: " + acquiredInfo
+ " message: " + message);
@@ -520,6 +521,10 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onSystemEvent(int event) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onSystemEvent after successful auth");
+ return;
+ }
if (!mPromptInfo.isReceiveSystemEvents()) {
return;
}
@@ -538,53 +543,35 @@ public final class AuthSession implements IBinder.DeathRecipient {
mState = STATE_AUTH_STARTED_UI_SHOWING;
- if (mMultiSensorMode == BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT) {
- mMultiSensorState = MULTI_SENSOR_STATE_FACE_SCANNING;
- } else {
- startFingerprintSensorsNow();
- }
- }
-
- // call anytime after onDialogAnimatedIn() to indicate it's appropriate to start the
- // fingerprint sensor (i.e. face auth has failed or is not available)
- void onStartFingerprint() {
- if (mMultiSensorMode != BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT) {
- Slog.e(TAG, "onStartFingerprint, unexpected mode: " + mMultiSensorMode);
- return;
- }
-
- if (mState != STATE_AUTH_STARTED
- && mState != STATE_AUTH_STARTED_UI_SHOWING
- && mState != STATE_AUTH_PAUSED
- && mState != STATE_ERROR_PENDING_SYSUI) {
- Slog.w(TAG, "onStartFingerprint, started from unexpected state: " + mState);
- }
-
- mMultiSensorState = MULTI_SENSOR_STATE_FP_SCANNING;
- startFingerprintSensorsNow();
- }
-
- // unguarded helper for the above methods only
- private void startFingerprintSensorsNow() {
startAllPreparedFingerprintSensors();
mState = STATE_AUTH_STARTED_UI_SHOWING;
}
void onTryAgainPressed() {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onTryAgainPressed after successful auth");
+ return;
+ }
+
if (mState != STATE_AUTH_PAUSED) {
Slog.w(TAG, "onTryAgainPressed, state: " + mState);
}
try {
- setSensorsToStateWaitingForCookie();
+ setSensorsToStateWaitingForCookie(true /* isTryAgain */);
mState = STATE_AUTH_PAUSED_RESUMING;
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException: " + e);
}
}
- void onAuthenticationSucceeded(int sensorId, boolean strong,
- byte[] token) {
+ void onAuthenticationSucceeded(int sensorId, boolean strong, byte[] token) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onAuthenticationSucceeded after successful auth");
+ return;
+ }
+
+ mAuthenticatedSensorId = sensorId;
if (strong) {
mTokenEscrow = token;
} else {
@@ -596,7 +583,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
try {
// Notify SysUI that the biometric has been authenticated. SysUI already knows
// the implicit/explicit state and will react accordingly.
- mStatusBarService.onBiometricAuthenticated();
+ mStatusBarService.onBiometricAuthenticated(sensorIdToModality(sensorId));
final boolean requireConfirmation = isConfirmationRequiredByAnyEligibleSensor();
@@ -609,20 +596,22 @@ public final class AuthSession implements IBinder.DeathRecipient {
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
+
+ cancelAllSensors(sensor -> sensor.id != sensorId);
}
- void onAuthenticationRejected() {
+ void onAuthenticationRejected(int sensorId) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onAuthenticationRejected after successful auth");
+ return;
+ }
+
try {
- mStatusBarService.onBiometricError(TYPE_NONE,
+ mStatusBarService.onBiometricError(sensorIdToModality(sensorId),
BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 0 /* vendorCode */);
-
- // TODO: This logic will need to be updated if BP is multi-modal
- if (hasPausableBiometric()) {
- // Pause authentication. onBiometricAuthenticated(false) causes the
- // dialog to show a "try again" button for passive modalities.
+ if (pauseSensorIfSupported(sensorId)) {
mState = STATE_AUTH_PAUSED;
}
-
mClientReceiver.onAuthenticationFailed();
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
@@ -630,15 +619,34 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onAuthenticationTimedOut after successful auth");
+ return;
+ }
+
try {
mStatusBarService.onBiometricError(sensorIdToModality(sensorId), error, vendorCode);
+ pauseSensorIfSupported(sensorId);
mState = STATE_AUTH_PAUSED;
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
}
+ private boolean pauseSensorIfSupported(int sensorId) {
+ if (sensorIdToModality(sensorId) == TYPE_FACE) {
+ cancelAllSensors(sensor -> sensor.id == sensorId);
+ return true;
+ }
+ return false;
+ }
+
void onDeviceCredentialPressed() {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onDeviceCredentialPressed after successful auth");
+ return;
+ }
+
// Cancel authentication. Skip the token/package check since we are cancelling
// from system server. The interface is permission protected so this is fine.
cancelAllSensors();
@@ -666,6 +674,10 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
}
+ private boolean hasAuthenticated() {
+ return mAuthenticatedSensorId != -1;
+ }
+
private void logOnDialogDismissed(@BiometricPrompt.DismissedReason int reason) {
if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
// Explicit auth, authentication confirmed.
@@ -794,6 +806,11 @@ public final class AuthSession implements IBinder.DeathRecipient {
* @return true if this AuthSession is finished, e.g. should be set to null
*/
boolean onCancelAuthSession(boolean force) {
+ if (hasAuthenticated()) {
+ Slog.d(TAG, "onCancelAuthSession after successful auth");
+ return true;
+ }
+
mCancelled = true;
final boolean authStarted = mState == STATE_AUTH_CALLED
@@ -848,15 +865,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
return remainingCookies == 0;
}
- private boolean hasPausableBiometric() {
- for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
- if (sensor.modality == TYPE_FACE) {
- return true;
- }
- }
- return false;
- }
-
@SessionState int getState() {
return mState;
}
@@ -919,7 +927,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
if (hasFace && hasFingerprint) {
- return BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;
+ return BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE;
}
return BIOMETRIC_MULTI_SENSOR_DEFAULT;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 0333c3e247c0..7166783f0b23 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -131,8 +131,10 @@ public abstract class BiometricSensor {
void goToStateCancelling(IBinder token, String opPackageName, long requestId)
throws RemoteException {
- impl.cancelAuthenticationFromService(token, opPackageName, requestId);
- mSensorState = STATE_CANCELING;
+ if (mSensorState != STATE_CANCELING) {
+ impl.cancelAuthenticationFromService(token, opPackageName, requestId);
+ mSensorState = STATE_CANCELING;
+ }
}
void goToStoppedStateIfCookieMatches(int cookie, int error) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 758cf7a7d430..0d9b75481ea9 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -55,7 +55,6 @@ import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -84,6 +83,7 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -92,22 +92,6 @@ public class BiometricService extends SystemService {
static final String TAG = "BiometricService";
- private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
- private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
- private static final int MSG_ON_ERROR = 4;
- private static final int MSG_ON_ACQUIRED = 5;
- private static final int MSG_ON_DISMISSED = 6;
- private static final int MSG_ON_TRY_AGAIN_PRESSED = 7;
- private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
- private static final int MSG_AUTHENTICATE = 9;
- private static final int MSG_CANCEL_AUTHENTICATION = 10;
- private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11;
- private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
- private static final int MSG_ON_SYSTEM_EVENT = 13;
- private static final int MSG_CLIENT_DIED = 14;
- private static final int MSG_ON_DIALOG_ANIMATED_IN = 15;
- private static final int MSG_ON_START_FINGERPRINT_NOW = 16;
-
private final Injector mInjector;
private final DevicePolicyManager mDevicePolicyManager;
@VisibleForTesting
@@ -116,7 +100,7 @@ public class BiometricService extends SystemService {
final SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private final Random mRandom = new Random();
- @NonNull private final AtomicLong mRequestCounter;
+ @NonNull private final Supplier<Long> mRequestCounter;
@VisibleForTesting
IStatusBarService mStatusBarService;
@@ -128,133 +112,13 @@ public class BiometricService extends SystemService {
// Get and cache the available biometric authenticators and their associated info.
final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
+ @VisibleForTesting
BiometricStrengthController mBiometricStrengthController;
// The current authentication session, null if idle/done.
@VisibleForTesting
- AuthSession mCurrentAuthSession;
-
- @VisibleForTesting
- final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ON_AUTHENTICATION_SUCCEEDED: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleAuthenticationSucceeded(
- args.argi1 /* sensorId */,
- (byte[]) args.arg1 /* token */);
- args.recycle();
- break;
- }
-
- case MSG_ON_AUTHENTICATION_REJECTED: {
- handleAuthenticationRejected();
- break;
- }
-
- case MSG_ON_ERROR: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleOnError(
- args.argi1 /* sensorId */,
- args.argi2 /* cookie */,
- args.argi3 /* error */,
- args.argi4 /* vendorCode */);
- args.recycle();
- break;
- }
-
- case MSG_ON_ACQUIRED: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleOnAcquired(
- args.argi1 /* sensorId */,
- args.argi2 /* acquiredInfo */,
- args.argi3 /* vendorCode */);
- args.recycle();
- break;
- }
-
- case MSG_ON_DISMISSED: {
- handleOnDismissed(msg.arg1, (byte[]) msg.obj);
- break;
- }
-
- case MSG_ON_TRY_AGAIN_PRESSED: {
- handleOnTryAgainPressed();
- break;
- }
-
- case MSG_ON_READY_FOR_AUTHENTICATION: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleOnReadyForAuthentication(
- args.argi1 /* cookie */);
- args.recycle();
- break;
- }
-
- case MSG_AUTHENTICATE: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleAuthenticate(
- (IBinder) args.arg1 /* token */,
- (long) args.arg6 /* requestId */,
- (long) args.arg2 /* operationId */,
- args.argi1 /* userid */,
- (IBiometricServiceReceiver) args.arg3 /* receiver */,
- (String) args.arg4 /* opPackageName */,
- (PromptInfo) args.arg5 /* promptInfo */);
- args.recycle();
- break;
- }
-
- case MSG_CANCEL_AUTHENTICATION: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleCancelAuthentication((long) args.arg3 /* requestId */);
- args.recycle();
- break;
- }
-
- case MSG_ON_AUTHENTICATION_TIMED_OUT: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleAuthenticationTimedOut(
- args.argi1 /* sensorId */,
- args.argi2 /* cookie */,
- args.argi3 /* error */,
- args.argi4 /* vendorCode */);
- args.recycle();
- break;
- }
-
- case MSG_ON_DEVICE_CREDENTIAL_PRESSED: {
- handleOnDeviceCredentialPressed();
- break;
- }
-
- case MSG_ON_SYSTEM_EVENT: {
- handleOnSystemEvent((int) msg.obj);
- break;
- }
-
- case MSG_CLIENT_DIED: {
- handleClientDied();
- break;
- }
-
- case MSG_ON_DIALOG_ANIMATED_IN: {
- handleOnDialogAnimatedIn();
- break;
- }
-
- case MSG_ON_START_FINGERPRINT_NOW: {
- handleOnStartFingerprintNow();
- break;
- }
-
- default:
- Slog.e(TAG, "Unknown message: " + msg);
- break;
- }
- }
- };
+ AuthSession mAuthSession;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
/**
* Tracks authenticatorId invalidation. For more details, see
@@ -552,93 +416,74 @@ public class BiometricService extends SystemService {
}
// Receives events from individual biometric sensors.
- @VisibleForTesting
- final IBiometricSensorReceiver mBiometricSensorReceiver = new IBiometricSensorReceiver.Stub() {
- @Override
- public void onAuthenticationSucceeded(int sensorId, byte[] token) {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = sensorId;
- args.arg1 = token;
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
- }
-
- @Override
- public void onAuthenticationFailed(int sensorId) {
- Slog.v(TAG, "onAuthenticationFailed");
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED).sendToTarget();
- }
+ private IBiometricSensorReceiver createBiometricSensorReceiver(final long requestId) {
+ return new IBiometricSensorReceiver.Stub() {
+ @Override
+ public void onAuthenticationSucceeded(int sensorId, byte[] token) {
+ mHandler.post(() -> handleAuthenticationSucceeded(requestId, sensorId, token));
+ }
- @Override
- public void onError(int sensorId, int cookie, @BiometricConstants.Errors int error,
- int vendorCode) {
- // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
- // soft errors and we should allow the user to try authenticating again instead of
- // dismissing BiometricPrompt.
- if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = sensorId;
- args.argi2 = cookie;
- args.argi3 = error;
- args.argi4 = vendorCode;
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, args).sendToTarget();
- } else {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = sensorId;
- args.argi2 = cookie;
- args.argi3 = error;
- args.argi4 = vendorCode;
- mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
+ @Override
+ public void onAuthenticationFailed(int sensorId) {
+ Slog.v(TAG, "onAuthenticationFailed");
+ mHandler.post(() -> handleAuthenticationRejected(requestId, sensorId));
}
- }
- @Override
- public void onAcquired(int sensorId, int acquiredInfo, int vendorCode) {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = sensorId;
- args.argi2 = acquiredInfo;
- args.argi3 = vendorCode;
- mHandler.obtainMessage(MSG_ON_ACQUIRED, args).sendToTarget();
- }
- };
+ @Override
+ public void onError(int sensorId, int cookie, @BiometricConstants.Errors int error,
+ int vendorCode) {
+ // Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
+ // soft errors and we should allow the user to try authenticating again instead of
+ // dismissing BiometricPrompt.
+ if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
+ mHandler.post(() -> handleAuthenticationTimedOut(
+ requestId, sensorId, cookie, error, vendorCode));
+ } else {
+ mHandler.post(() -> handleOnError(
+ requestId, sensorId, cookie, error, vendorCode));
+ }
+ }
- final IBiometricSysuiReceiver mSysuiReceiver = new IBiometricSysuiReceiver.Stub() {
- @Override
- public void onDialogDismissed(@BiometricPrompt.DismissedReason int reason,
- @Nullable byte[] credentialAttestation) {
- mHandler.obtainMessage(MSG_ON_DISMISSED,
- reason,
- 0 /* arg2 */,
- credentialAttestation /* obj */).sendToTarget();
- }
+ @Override
+ public void onAcquired(int sensorId, int acquiredInfo, int vendorCode) {
+ mHandler.post(() -> handleOnAcquired(
+ requestId, sensorId, acquiredInfo, vendorCode));
+ }
+ };
+ }
- @Override
- public void onTryAgainPressed() {
- mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
- }
+ private IBiometricSysuiReceiver createSysuiReceiver(final long requestId) {
+ return new IBiometricSysuiReceiver.Stub() {
+ @Override
+ public void onDialogDismissed(@BiometricPrompt.DismissedReason int reason,
+ @Nullable byte[] credentialAttestation) {
+ mHandler.post(() -> handleOnDismissed(requestId, reason, credentialAttestation));
+ }
- @Override
- public void onDeviceCredentialPressed() {
- mHandler.sendEmptyMessage(MSG_ON_DEVICE_CREDENTIAL_PRESSED);
- }
+ @Override
+ public void onTryAgainPressed() {
+ mHandler.post(() -> handleOnTryAgainPressed(requestId));
+ }
- @Override
- public void onSystemEvent(int event) {
- mHandler.obtainMessage(MSG_ON_SYSTEM_EVENT, event).sendToTarget();
- }
+ @Override
+ public void onDeviceCredentialPressed() {
+ mHandler.post(() -> handleOnDeviceCredentialPressed(requestId));
+ }
- @Override
- public void onDialogAnimatedIn() {
- mHandler.obtainMessage(MSG_ON_DIALOG_ANIMATED_IN).sendToTarget();
- }
+ @Override
+ public void onSystemEvent(int event) {
+ mHandler.post(() -> handleOnSystemEvent(requestId, event));
+ }
- @Override
- public void onStartFingerprintNow() {
- mHandler.obtainMessage(MSG_ON_START_FINGERPRINT_NOW).sendToTarget();
- }
- };
+ @Override
+ public void onDialogAnimatedIn() {
+ mHandler.post(() -> handleOnDialogAnimatedIn(requestId));
+ }
+ };
+ }
- private final AuthSession.ClientDeathReceiver mClientDeathReceiver = () -> {
- mHandler.sendEmptyMessage(MSG_CLIENT_DIED);
+ private AuthSession.ClientDeathReceiver createClientDeathReceiver(final long requestId) {
+ return () -> mHandler.post(() -> handleClientDied(requestId));
};
/**
@@ -679,12 +524,10 @@ public class BiometricService extends SystemService {
}
@Override // Binder call
- public void onReadyForAuthentication(int cookie) {
+ public void onReadyForAuthentication(long requestId, int cookie) {
checkInternalPermission();
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = cookie;
- mHandler.obtainMessage(MSG_ON_READY_FOR_AUTHENTICATION, args).sendToTarget();
+ mHandler.post(() -> handleOnReadyForAuthentication(requestId, cookie));
}
@Override // Binder call
@@ -711,18 +554,9 @@ public class BiometricService extends SystemService {
}
}
- final long requestId = mRequestCounter.incrementAndGet();
-
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = token;
- args.arg2 = operationId;
- args.argi1 = userId;
- args.arg3 = receiver;
- args.arg4 = opPackageName;
- args.arg5 = promptInfo;
- args.arg6 = requestId;
-
- mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
+ final long requestId = mRequestCounter.get();
+ mHandler.post(() -> handleAuthenticate(
+ token, requestId, operationId, userId, receiver, opPackageName, promptInfo));
return requestId;
}
@@ -736,7 +570,7 @@ public class BiometricService extends SystemService {
args.arg2 = opPackageName;
args.arg3 = requestId;
- mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
+ mHandler.post(() -> handleCancelAuthentication(requestId));
}
@Override // Binder call
@@ -1002,8 +836,7 @@ public class BiometricService extends SystemService {
Slog.d(TAG, "ClearSchedulerBuffer: " + clearSchedulerBuffer);
final ProtoOutputStream proto = new ProtoOutputStream(fd);
proto.write(BiometricServiceStateProto.AUTH_SESSION_STATE,
- mCurrentAuthSession != null ? mCurrentAuthSession.getState()
- : STATE_AUTH_IDLE);
+ mAuthSession != null ? mAuthSession.getState() : STATE_AUTH_IDLE);
for (BiometricSensor sensor : mSensors) {
byte[] serviceState = sensor.impl
.dumpSensorServiceStateProto(clearSchedulerBuffer);
@@ -1128,8 +961,9 @@ public class BiometricService extends SystemService {
CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
}
- public AtomicLong getRequestGenerator() {
- return new AtomicLong(0);
+ public Supplier<Long> getRequestGenerator() {
+ final AtomicLong generator = new AtomicLong(0);
+ return () -> generator.incrementAndGet();
}
}
@@ -1202,172 +1036,184 @@ public class BiometricService extends SystemService {
return false;
}
- private void handleAuthenticationSucceeded(int sensorId, byte[] token) {
+ @Nullable
+ private AuthSession getAuthSessionIfCurrent(long requestId) {
+ final AuthSession session = mAuthSession;
+ if (session != null && session.getRequestId() == requestId) {
+ return session;
+ }
+ return null;
+ }
+
+ private void handleAuthenticationSucceeded(long requestId, int sensorId, byte[] token) {
Slog.v(TAG, "handleAuthenticationSucceeded(), sensorId: " + sensorId);
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
Slog.e(TAG, "handleAuthenticationSucceeded: AuthSession is null");
return;
}
- mCurrentAuthSession.onAuthenticationSucceeded(sensorId, isStrongBiometric(sensorId), token);
+ session.onAuthenticationSucceeded(sensorId, isStrongBiometric(sensorId), token);
}
- private void handleAuthenticationRejected() {
+ private void handleAuthenticationRejected(long requestId, int sensorId) {
Slog.v(TAG, "handleAuthenticationRejected()");
// Should never happen, log this to catch bad HAL behavior (e.g. auth rejected
// after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleAuthenticationRejected: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleAuthenticationRejected: AuthSession is not current");
return;
}
- mCurrentAuthSession.onAuthenticationRejected();
+ session.onAuthenticationRejected(sensorId);
}
- private void handleAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) {
+ private void handleAuthenticationTimedOut(long requestId, int sensorId, int cookie, int error,
+ int vendorCode) {
Slog.v(TAG, "handleAuthenticationTimedOut(), sensorId: " + sensorId
+ ", cookie: " + cookie
+ ", error: " + error
+ ", vendorCode: " + vendorCode);
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleAuthenticationTimedOut: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleAuthenticationTimedOut: AuthSession is not current");
return;
}
- mCurrentAuthSession.onAuthenticationTimedOut(sensorId, cookie, error, vendorCode);
+ session.onAuthenticationTimedOut(sensorId, cookie, error, vendorCode);
}
- private void handleOnError(int sensorId, int cookie, @BiometricConstants.Errors int error,
- int vendorCode) {
+ private void handleOnError(long requestId, int sensorId, int cookie,
+ @BiometricConstants.Errors int error, int vendorCode) {
Slog.d(TAG, "handleOnError() sensorId: " + sensorId
+ ", cookie: " + cookie
+ ", error: " + error
+ ", vendorCode: " + vendorCode);
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnError: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleOnError: AuthSession is not current");
return;
}
try {
- final boolean finished = mCurrentAuthSession
- .onErrorReceived(sensorId, cookie, error, vendorCode);
+ final boolean finished = session.onErrorReceived(sensorId, cookie, error, vendorCode);
if (finished) {
Slog.d(TAG, "handleOnError: AuthSession finished");
- mCurrentAuthSession = null;
+ mAuthSession = null;
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
}
- private void handleOnAcquired(int sensorId, int acquiredInfo, int vendorCode) {
+ private void handleOnAcquired(long requestId, int sensorId, int acquiredInfo, int vendorCode) {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAcquired: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "onAcquired: AuthSession is not current");
return;
}
- mCurrentAuthSession.onAcquired(sensorId, acquiredInfo, vendorCode);
+ session.onAcquired(sensorId, acquiredInfo, vendorCode);
}
- private void handleOnDismissed(@BiometricPrompt.DismissedReason int reason,
+ private void handleOnDismissed(long requestId, @BiometricPrompt.DismissedReason int reason,
@Nullable byte[] credentialAttestation) {
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onDismissed: " + reason + ", AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.e(TAG, "onDismissed: " + reason + ", AuthSession is not current");
return;
}
- mCurrentAuthSession.onDialogDismissed(reason, credentialAttestation);
- mCurrentAuthSession = null;
+ session.onDialogDismissed(reason, credentialAttestation);
+ mAuthSession = null;
}
- private void handleOnTryAgainPressed() {
+ private void handleOnTryAgainPressed(long requestId) {
Slog.d(TAG, "onTryAgainPressed");
// No need to check permission, since it can only be invoked by SystemUI
// (or system server itself).
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnTryAgainPressed: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleOnTryAgainPressed: AuthSession is not current");
return;
}
- mCurrentAuthSession.onTryAgainPressed();
+ session.onTryAgainPressed();
}
- private void handleOnDeviceCredentialPressed() {
+ private void handleOnDeviceCredentialPressed(long requestId) {
Slog.d(TAG, "onDeviceCredentialPressed");
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnDeviceCredentialPressed: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleOnDeviceCredentialPressed: AuthSession is not current");
return;
}
- mCurrentAuthSession.onDeviceCredentialPressed();
+ session.onDeviceCredentialPressed();
}
- private void handleOnSystemEvent(int event) {
+ private void handleOnSystemEvent(long requestId, int event) {
Slog.d(TAG, "onSystemEvent: " + event);
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnSystemEvent: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleOnSystemEvent: AuthSession is not current");
return;
}
- mCurrentAuthSession.onSystemEvent(event);
+ session.onSystemEvent(event);
}
- private void handleClientDied() {
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleClientDied: AuthSession is null");
+ private void handleClientDied(long requestId) {
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleClientDied: AuthSession is not current");
return;
}
- Slog.e(TAG, "Session: " + mCurrentAuthSession);
- final boolean finished = mCurrentAuthSession.onClientDied();
+ Slog.e(TAG, "Session: " + session);
+ final boolean finished = session.onClientDied();
if (finished) {
- mCurrentAuthSession = null;
+ mAuthSession = null;
}
}
- private void handleOnDialogAnimatedIn() {
+ private void handleOnDialogAnimatedIn(long requestId) {
Slog.d(TAG, "handleOnDialogAnimatedIn");
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnDialogAnimatedIn: AuthSession is null");
- return;
- }
-
- mCurrentAuthSession.onDialogAnimatedIn();
- }
- private void handleOnStartFingerprintNow() {
- Slog.d(TAG, "handleOnStartFingerprintNow");
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleOnStartFingerprintNow: AuthSession is null");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleOnDialogAnimatedIn: AuthSession is not current");
return;
}
- mCurrentAuthSession.onStartFingerprint();
+ session.onDialogAnimatedIn();
}
/**
* Invoked when each service has notified that its client is ready to be started. When
* all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
*/
- private void handleOnReadyForAuthentication(int cookie) {
- if (mCurrentAuthSession == null) {
+ private void handleOnReadyForAuthentication(long requestId, int cookie) {
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
// Only should happen if a biometric was locked out when authenticate() was invoked.
// In that case, if device credentials are allowed, the UI is already showing. If not
// allowed, the error has already been returned to the caller.
- Slog.w(TAG, "handleOnReadyForAuthentication: AuthSession is null");
+ Slog.w(TAG, "handleOnReadyForAuthentication: AuthSession is not current");
return;
}
- mCurrentAuthSession.onCookieReceived(cookie);
+ session.onCookieReceived(cookie);
}
private void handleAuthenticate(IBinder token, long requestId, long operationId, int userId,
@@ -1428,47 +1274,41 @@ public class BiometricService extends SystemService {
// No need to dismiss dialog / send error yet if we're continuing authentication, e.g.
// "Try again" is showing due to something like ERROR_TIMEOUT.
- if (mCurrentAuthSession != null) {
+ if (mAuthSession != null) {
// Forcefully cancel authentication. Dismiss the UI, and immediately send
// ERROR_CANCELED to the client. Note that we should/will ignore HAL ERROR_CANCELED.
// Expect to see some harmless "unknown cookie" errors.
- Slog.w(TAG, "Existing AuthSession: " + mCurrentAuthSession);
- mCurrentAuthSession.onCancelAuthSession(true /* force */);
- mCurrentAuthSession = null;
+ Slog.w(TAG, "Existing AuthSession: " + mAuthSession);
+ mAuthSession.onCancelAuthSession(true /* force */);
+ mAuthSession = null;
}
final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId);
- mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver,
- mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, requestId,
- operationId, userId, mBiometricSensorReceiver, receiver, opPackageName, promptInfo,
- debugEnabled, mInjector.getFingerprintSensorProperties(getContext()));
+ mAuthSession = new AuthSession(getContext(), mStatusBarService,
+ createSysuiReceiver(requestId), mKeyStore, mRandom,
+ createClientDeathReceiver(requestId), preAuthInfo, token, requestId,
+ operationId, userId, createBiometricSensorReceiver(requestId), receiver,
+ opPackageName, promptInfo, debugEnabled,
+ mInjector.getFingerprintSensorProperties(getContext()));
try {
- mCurrentAuthSession.goToInitialState();
+ mAuthSession.goToInitialState();
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
}
private void handleCancelAuthentication(long requestId) {
- if (mCurrentAuthSession == null) {
- Slog.e(TAG, "handleCancelAuthentication: AuthSession is null");
- return;
- }
- if (mCurrentAuthSession.getRequestId() != requestId) {
- // TODO: actually cancel the operation
- // This can happen if the operation has been queued, but is cancelled before
- // it reaches the head of the scheduler. Consider it a programming error for now
- // and ignore it.
- Slog.e(TAG, "handleCancelAuthentication: AuthSession mismatch current requestId: "
- + mCurrentAuthSession.getRequestId() + " cancel for: " + requestId
- + " (ignoring cancellation)");
+ final AuthSession session = getAuthSessionIfCurrent(requestId);
+ if (session == null) {
+ Slog.w(TAG, "handleCancelAuthentication: AuthSession is not current");
+ // TODO: actually cancel the operation?
return;
}
- final boolean finished = mCurrentAuthSession.onCancelAuthSession(false /* force */);
+ final boolean finished = session.onCancelAuthSession(false /* force */);
if (finished) {
Slog.d(TAG, "handleCancelAuthentication: AuthSession finished");
- mCurrentAuthSession = null;
+ mAuthSession = null;
}
}
@@ -1491,7 +1331,7 @@ public class BiometricService extends SystemService {
pw.println(" " + sensor);
}
pw.println();
- pw.println("CurrentSession: " + mCurrentAuthSession);
+ pw.println("CurrentSession: " + mAuthSession);
pw.println();
pw.println("CoexCoordinator: " + CoexCoordinator.getInstance().toString());
pw.println();
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 54b79e1f8e4a..6d687726dbe8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -86,6 +86,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
private long mStartTimeMs;
private boolean mAuthAttempted;
+ private boolean mAuthSuccess = false;
// TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
// the state. We should think of a way to improve this in the future.
@@ -237,6 +238,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
"Successful background authentication!");
}
+ mAuthSuccess = true;
markAlreadyDone();
if (mTaskStackListener != null) {
@@ -502,6 +504,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
return mAuthAttempted;
}
+ /** If an auth attempt completed successfully. */
+ public boolean wasAuthSuccessful() {
+ return mAuthSuccess;
+ }
+
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 1a6da94f683e..d0ec4470d3e6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -316,7 +316,8 @@ public class BiometricScheduler {
}
} else {
try {
- mBiometricService.onReadyForAuthentication(cookie);
+ mBiometricService.onReadyForAuthentication(
+ mCurrentOperation.getClientMonitor().getRequestId(), cookie);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index 812ca8ac62fe..15f0cadced99 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -84,6 +84,8 @@ public class BiometricSchedulerOperation {
private final BaseClientMonitor mClientMonitor;
@Nullable
private final ClientMonitorCallback mClientCallback;
+ @Nullable
+ private ClientMonitorCallback mOnStartCallback;
@OperationState
private int mState;
@VisibleForTesting
@@ -108,7 +110,8 @@ public class BiometricSchedulerOperation {
mCancelWatchdog = () -> {
if (!isFinished()) {
Slog.e(TAG, "[Watchdog Triggered]: " + this);
- getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+ getWrappedCallback(mOnStartCallback)
+ .onClientFinished(mClientMonitor, false /* success */);
}
};
}
@@ -174,6 +177,7 @@ public class BiometricSchedulerOperation {
}
private boolean doStart(@NonNull ClientMonitorCallback callback) {
+ mOnStartCallback = callback;
final ClientMonitorCallback cb = getWrappedCallback(callback);
if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index f1c786b4977c..46d863d7aaec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -123,7 +123,8 @@ public class ClientMonitorCallbackConverter {
}
}
- void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
+ /** Called when a user has been removed. */
+ public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onRemoved((Face) identifier, remaining);
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
index 25d4a38cd475..5aa9b79c074c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
@@ -173,18 +173,13 @@ public class CoexCoordinator {
}
// SensorType to AuthenticationClient map
- private final Map<Integer, AuthenticationClient<?>> mClientMap;
- @VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths;
+ private final Map<Integer, AuthenticationClient<?>> mClientMap = new HashMap<>();
+ @VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths = new LinkedList<>();
private boolean mAdvancedLogicEnabled;
private boolean mFaceHapticDisabledWhenNonBypass;
- private final Handler mHandler;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
- private CoexCoordinator() {
- // Singleton
- mClientMap = new HashMap<>();
- mSuccessfulAuths = new LinkedList<>();
- mHandler = new Handler(Looper.getMainLooper());
- }
+ private CoexCoordinator() {}
public void addAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
@NonNull AuthenticationClient<?> client) {
@@ -221,8 +216,14 @@ public class CoexCoordinator {
public void onAuthenticationSucceeded(long currentTimeMillis,
@NonNull AuthenticationClient<?> client,
@NonNull Callback callback) {
+ final boolean isUsingSingleModality = isSingleAuthOnly(client);
+
if (client.isBiometricPrompt()) {
- callback.sendHapticFeedback();
+ if (!isUsingSingleModality && hasMultipleSuccessfulAuthentications()) {
+ // only send feedback on the first one
+ } else {
+ callback.sendHapticFeedback();
+ }
// For BP, BiometricService will add the authToken to Keystore.
callback.sendAuthenticationResult(false /* addAuthTokenIfStrong */);
callback.handleLifecycleAfterAuth();
@@ -234,7 +235,7 @@ public class CoexCoordinator {
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
callback.handleLifecycleAfterAuth();
} else if (mAdvancedLogicEnabled && client.isKeyguard()) {
- if (isSingleAuthOnly(client)) {
+ if (isUsingSingleModality) {
// Single sensor authentication
callback.sendHapticFeedback();
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
@@ -295,10 +296,10 @@ public class CoexCoordinator {
@NonNull AuthenticationClient<?> client,
@LockoutTracker.LockoutMode int lockoutMode,
@NonNull Callback callback) {
- final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard();
+ final boolean isUsingSingleModality = isSingleAuthOnly(client);
- if (keyguardAdvancedLogic) {
- if (isSingleAuthOnly(client)) {
+ if (mAdvancedLogicEnabled && client.isKeyguard()) {
+ if (isUsingSingleModality) {
callback.sendHapticFeedback();
callback.handleLifecycleAfterAuth();
} else {
@@ -319,8 +320,7 @@ public class CoexCoordinator {
// also done now.
callback.sendHapticFeedback();
callback.handleLifecycleAfterAuth();
- }
- else {
+ } else {
// UDFPS auth has never been attempted.
if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
Slog.w(TAG, "Skipping face reject haptic");
@@ -360,6 +360,11 @@ public class CoexCoordinator {
callback.handleLifecycleAfterAuth();
}
}
+ } else if (client.isBiometricPrompt() && !isUsingSingleModality) {
+ if (!isCurrentFaceAuth(client)) {
+ callback.sendHapticFeedback();
+ }
+ callback.handleLifecycleAfterAuth();
} else {
callback.sendHapticFeedback();
callback.handleLifecycleAfterAuth();
@@ -380,6 +385,8 @@ public class CoexCoordinator {
*/
public void onAuthenticationError(@NonNull AuthenticationClient<?> client,
@BiometricConstants.Errors int error, @NonNull ErrorCallback callback) {
+ final boolean isUsingSingleModality = isSingleAuthOnly(client);
+
// Figure out non-coex state
final boolean shouldUsuallyVibrate;
if (isCurrentFaceAuth(client)) {
@@ -401,25 +408,26 @@ public class CoexCoordinator {
}
// Figure out coex state
- final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard();
final boolean hapticSuppressedByCoex;
-
- if (keyguardAdvancedLogic) {
- if (isSingleAuthOnly(client)) {
+ if (mAdvancedLogicEnabled && client.isKeyguard()) {
+ if (isUsingSingleModality) {
hapticSuppressedByCoex = false;
} else {
hapticSuppressedByCoex = isCurrentFaceAuth(client)
&& !client.isKeyguardBypassEnabled();
}
+ } else if (client.isBiometricPrompt() && !isUsingSingleModality) {
+ hapticSuppressedByCoex = isCurrentFaceAuth(client);
} else {
hapticSuppressedByCoex = false;
}
// Combine and send feedback if appropriate
- Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate
- + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex);
if (shouldUsuallyVibrate && !hapticSuppressedByCoex) {
callback.sendHapticFeedback();
+ } else {
+ Slog.v(TAG, "no haptic shouldUsuallyVibrate: " + shouldUsuallyVibrate
+ + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex);
}
}
@@ -504,6 +512,19 @@ public class CoexCoordinator {
return true;
}
+ private boolean hasMultipleSuccessfulAuthentications() {
+ int count = 0;
+ for (AuthenticationClient<?> c : mClientMap.values()) {
+ if (c.wasAuthSuccessful()) {
+ count++;
+ }
+ if (count > 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 07ce841a7cac..e0d519469e32 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -71,6 +72,24 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
@Override
public void onRemoved(@NonNull BiometricAuthenticator.Identifier identifier, int remaining) {
+ // This happens when we have failed to remove a biometric.
+ if (identifier == null) {
+ Slog.e(TAG, "identifier was null, skipping onRemove()");
+ try {
+ if (getListener() != null) {
+ getListener().onError(getSensorId(), getCookie(),
+ BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_REMOVE,
+ 0 /* vendorCode */);
+ } else {
+ Slog.e(TAG, "Error, listener was null, not sending onError callback");
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to send error to client for onRemoved", e);
+ }
+ mCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
Slog.d(TAG, "onRemoved: " + identifier.getBiometricId() + " remaining: " + remaining);
mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
identifier.getBiometricId());
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9bfdd687644b..66e9da040102 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2579,7 +2579,7 @@ public final class DisplayManagerService extends SystemService {
boolean getAllowNonNativeRefreshRateOverride() {
return DisplayProperties
- .debug_allow_non_native_refresh_rate_override().orElse(false);
+ .debug_allow_non_native_refresh_rate_override().orElse(true);
}
@NonNull
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9e00f95c9c6f..29e5f922b468 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -48,6 +48,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -168,10 +169,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private final SparseBooleanArray mDisabledByUserRestriction;
/**
- * Cache of services per user id.
+ * Cache of service list per user id.
*/
@GuardedBy("mLock")
- private final SparseArray<S> mServicesCache = new SparseArray<>();
+ private final SparseArray<List<S>> mServicesCacheList = new SparseArray<>();
/**
* Value that determines whether the per-user service should be removed from the cache when its
@@ -252,8 +253,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) {
mServiceNameResolver.setOnTemporaryServiceNameChangedCallback(
- (u, s, t) -> onServiceNameChanged(u, s, t));
-
+ this::onServiceNameChanged);
}
if (disallowProperty == null) {
mDisabledByUserRestriction = null;
@@ -308,7 +308,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override // from SystemService
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
- removeCachedServiceLocked(user.getUserIdentifier());
+ removeCachedServiceListLocked(user.getUserIdentifier());
}
}
@@ -386,21 +386,58 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
}
}
/**
+ * Temporarily sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + Arrays.toString(componentNames)
+ + " for " + durationMs + "ms");
+ if (mServiceNameResolver == null) {
+ return;
+ }
+ enforceCallingPermissionForManagement();
+
+ Objects.requireNonNull(componentNames);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S oldService = peekServiceForUserLocked(userId);
+ if (oldService != null) {
+ oldService.removeSelfFromCache();
+ }
+ mServiceNameResolver.setTemporaryServices(userId, componentNames, durationMs);
+ }
+ }
+
+ /**
* Sets whether the default service should be used.
*
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @throws SecurityException if caller is not allowed to manage this service's settings.
- *
* @return whether the enabled state changed.
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
@@ -420,7 +457,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
// Must update the service on cache so its initialization code is triggered
@@ -501,6 +538,21 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
+ * Creates a new service list that will be added to the cache.
+ *
+ * @param resolvedUserId the resolved user id for the service.
+ * @param disabled whether the service is currently disabled (due to {@link UserManager}
+ * restrictions).
+ * @return a new instance.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected List<S> newServiceListLocked(@UserIdInt int resolvedUserId, boolean disabled,
+ String[] serviceNames) {
+ throw new UnsupportedOperationException("newServiceListLocked not implemented. ");
+ }
+
+ /**
* Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}, which are automatically handled).
@@ -516,7 +568,6 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}.
- *
*/
@SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@@ -527,7 +578,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* Callback for Settings changes that were registered though
* {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
*
- * @param userId user associated with the change
+ * @param userId user associated with the change
* @param property Settings property changed.
*/
protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
@@ -539,18 +590,38 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) {
+ List<S> services = getServiceListForUserLocked(userId);
+ return services == null || services.size() == 0 ? null : services.get(0);
+ }
+
+ /**
+ * Gets the service instance list for a user, creating instances if not present in the cache.
+ */
+ @GuardedBy("mLock")
+ protected List<S> getServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- S service = mServicesCache.get(resolvedUserId);
- if (service == null) {
+ List<S> services = mServicesCacheList.get(resolvedUserId);
+ if (services == null || services.size() == 0) {
final boolean disabled = isDisabledLocked(userId);
- service = newServiceLocked(resolvedUserId, disabled);
+ if (mServiceNameResolver == null) {
+ return null;
+ }
+ if (mServiceNameResolver.isConfiguredInMultipleMode()) {
+ services = newServiceListLocked(resolvedUserId, disabled,
+ mServiceNameResolver.getServiceNameList(userId));
+ } else {
+ services = new ArrayList<>();
+ services.add(newServiceLocked(resolvedUserId, disabled));
+ }
if (!disabled) {
- onServiceEnabledLocked(service, resolvedUserId);
+ for (int i = 0; i < services.size(); i++) {
+ onServiceEnabledLocked(services.get(i), resolvedUserId);
+ }
}
- mServicesCache.put(userId, service);
+ mServicesCacheList.put(userId, services);
}
- return service;
+ return services;
}
/**
@@ -560,9 +631,20 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@GuardedBy("mLock")
@Nullable
protected S peekServiceForUserLocked(@UserIdInt int userId) {
+ List<S> serviceList = peekServiceListForUserLocked(userId);
+ return serviceList == null || serviceList.size() == 0 ? null : serviceList.get(0);
+ }
+
+ /**
+ * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
+ * present in the cache.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ protected List<S> peekServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- return mServicesCache.get(resolvedUserId);
+ return mServicesCacheList.get(resolvedUserId);
}
/**
@@ -570,36 +652,59 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void updateCachedServiceLocked(@UserIdInt int userId) {
- updateCachedServiceLocked(userId, isDisabledLocked(userId));
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
/**
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
+ @GuardedBy("mLock")
protected boolean isDisabledLocked(@UserIdInt int userId) {
- return mDisabledByUserRestriction == null ? false : mDisabledByUserRestriction.get(userId);
+ return mDisabledByUserRestriction != null && mDisabledByUserRestriction.get(userId);
}
/**
* Updates a cached service for a given user.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
- if (service != null) {
- service.updateLocked(disabled);
- if (!service.isEnabledLocked()) {
- removeCachedServiceLocked(userId);
- } else {
- onServiceEnabledLocked(service, userId);
+ updateCachedServiceListLocked(userId, disabled);
+ return service;
+ }
+
+ /**
+ * Updates a cached service for a given user.
+ *
+ * @param userId user handle.
+ * @param disabled whether the user is disabled.
+ * @return service for the user.
+ */
+ @GuardedBy("mLock")
+ protected List<S> updateCachedServiceListLocked(@UserIdInt int userId, boolean disabled) {
+ final List<S> services = getServiceListForUserLocked(userId);
+ if (services == null) {
+ return null;
+ }
+ for (int i = 0; i < services.size(); i++) {
+ S service = services.get(i);
+ if (service != null) {
+ synchronized (service.mLock) {
+ service.updateLocked(disabled);
+ if (!service.isEnabledLocked()) {
+ removeCachedServiceListLocked(userId);
+ } else {
+ onServiceEnabledLocked(services.get(i), userId);
+ }
+ }
}
}
- return service;
+ return services;
}
/**
@@ -619,28 +724,32 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
+ @GuardedBy("mLock")
protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
- * Removes a cached service for a given user.
+ * Removes a cached service list for a given user.
*
* @return the removed service.
*/
@GuardedBy("mLock")
@NonNull
- protected final S removeCachedServiceLocked(@UserIdInt int userId) {
- final S service = peekServiceForUserLocked(userId);
- if (service != null) {
- mServicesCache.delete(userId);
- onServiceRemoved(service, userId);
+ protected final List<S> removeCachedServiceListLocked(@UserIdInt int userId) {
+ final List<S> services = peekServiceListForUserLocked(userId);
+ if (services != null) {
+ mServicesCacheList.delete(userId);
+ for (int i = 0; i < services.size(); i++) {
+ onServiceRemoved(services.get(i), userId);
+ }
}
- return service;
+ return services;
}
/**
* Called before the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatingLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdatingLocked(" + userId + ")");
}
@@ -648,6 +757,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdated(" + userId + ")");
}
@@ -655,6 +765,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package data that provides the service for the given user is cleared.
*/
+ @GuardedBy("mLock")
protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")");
}
@@ -662,6 +773,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
/**
* Called after the package that provides the service for the given user is restarted.
*/
+ @GuardedBy("mLock")
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
}
@@ -679,14 +791,31 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
* <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
* that same method, or {@code super.onServiceNameChanged()}.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param serviceName the new service name.
* @param isTemporary whether the new service is temporary.
*/
protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName,
boolean isTemporary) {
synchronized (mLock) {
- updateCachedServiceLocked(userId);
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
+ }
+ }
+
+ /**
+ * Called when the service name list has changed (typically when using temporary services).
+ *
+ * <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
+ * that same method, or {@code super.onServiceNameChanged()}.
+ *
+ * @param userId user handle.
+ * @param serviceNames the new service name list.
+ * @param isTemporary whether the new service is temporary.
+ */
+ protected void onServiceNameListChanged(@UserIdInt int userId, @Nullable String[] serviceNames,
+ boolean isTemporary) {
+ synchronized (mLock) {
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
}
@@ -695,9 +824,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
- final int size = mServicesCache.size();
+ final int size = mServicesCacheList.size();
for (int i = 0; i < size; i++) {
- visitor.visit(mServicesCache.valueAt(i));
+ List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ visitor.visit(services.get(j));
+ }
}
}
@@ -706,7 +838,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected void clearCacheLocked() {
- mServicesCache.clear();
+ mServicesCacheList.clear();
}
/**
@@ -757,6 +889,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
// TODO(b/117779333): support proto
+ @GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
@@ -765,40 +898,64 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
try {
// Temporarily turn on full logging;
debug = verbose = true;
- final int size = mServicesCache.size();
- pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
- pw.print(" Verbose: "); pw.println(realVerbose);
- pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags);
+ final int size = mServicesCacheList.size();
+ pw.print(prefix);
+ pw.print("Debug: ");
+ pw.print(realDebug);
+ pw.print(" Verbose: ");
+ pw.println(realVerbose);
+ pw.print("Package policy flags: ");
+ pw.println(mServicePackagePolicyFlags);
if (mUpdatingPackageNames != null) {
- pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
+ pw.print("Packages being updated: ");
+ pw.println(mUpdatingPackageNames);
}
dumpSupportedUsers(pw, prefix);
if (mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mServiceNameResolver.dumpShort(pw); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mServiceNameResolver.dumpShort(pw);
+ pw.println();
final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
- pw.print(prefix2); pw.print(userId); pw.print(": ");
- mServiceNameResolver.dumpShort(pw, userId); pw.println();
+ pw.print(prefix2);
+ pw.print(userId);
+ pw.print(": ");
+ mServiceNameResolver.dumpShort(pw, userId);
+ pw.println();
}
}
- pw.print(prefix); pw.print("Users disabled by restriction: ");
+ pw.print(prefix);
+ pw.print("Users disabled by restriction: ");
pw.println(mDisabledByUserRestriction);
- pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+ pw.print(prefix);
+ pw.print("Allow instant service: ");
+ pw.println(mAllowInstantService);
final String settingsProperty = getServiceSettingsProperty();
if (settingsProperty != null) {
- pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
+ pw.print(prefix);
+ pw.print("Settings property: ");
+ pw.println(settingsProperty);
}
- pw.print(prefix); pw.print("Cached services: ");
+ pw.print(prefix);
+ pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
} else {
pw.println(size);
for (int i = 0; i < size; i++) {
- pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
- final S service = mServicesCache.valueAt(i);
- service.dumpLocked(prefix2, pw);
+ pw.print(prefix);
+ pw.print("Service at ");
+ pw.print(i);
+ pw.println(": ");
+ final List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ S service = services.get(i);
+ synchronized (service.mLock) {
+ service.dumpLocked(prefix2, pw);
+ }
+ }
pw.println();
}
}
@@ -820,7 +977,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
final int userId = getChangingUserId();
synchronized (mLock) {
if (mUpdatingPackageNames == null) {
- mUpdatingPackageNames = new SparseArray<String>(mServicesCache.size());
+ mUpdatingPackageNames = new SparseArray<String>(mServicesCacheList.size());
}
mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId);
@@ -835,7 +992,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being updated");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
!= 0) {
@@ -901,7 +1058,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
handleActiveServiceRestartedLocked(activePackageName, userId);
} else {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
} else {
handlePackageUpdateLocked(pkg);
@@ -930,7 +1087,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
private void handleActiveServiceRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) {
@@ -939,6 +1096,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
+ @GuardedBy("mLock")
private void handleActiveServiceRestartedLocked(String activePackageName,
@UserIdInt int userId) {
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
@@ -952,7 +1110,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
+ " because package " + activePackageName
+ " is being restarted");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
if (debug) {
@@ -966,14 +1124,27 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
@Override
public void onPackageModified(String packageName) {
- if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+ synchronized (mLock) {
+ if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
- if (mServiceNameResolver == null) {
- return;
+ if (mServiceNameResolver == null) {
+ return;
+ }
+
+ final int userId = getChangingUserId();
+ final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
+ userId);
+ if (serviceNames != null) {
+ for (int i = 0; i < serviceNames.length; i++) {
+ peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
+ }
+ }
}
+ }
- final int userId = getChangingUserId();
- final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
+ @GuardedBy("mLock")
+ private void peekAndUpdateCachedServiceLocked(String packageName, int userId,
+ String serviceName) {
if (serviceName == null) {
return;
}
@@ -997,6 +1168,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
}
}
+ @GuardedBy("mLock")
private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
@@ -1017,7 +1189,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
};
// package changes
- monitor.register(getContext(), null, UserHandle.ALL, true);
+ monitor.register(getContext(), null, UserHandle.ALL, true);
}
/**
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 757a5cca0817..58413c9c5705 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -43,14 +43,13 @@ import java.io.PrintWriter;
*
* @param <M> "main" service class.
* @param <S> "real" service class.
- *
* @hide
*/
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
- protected final @UserIdInt int mUserId;
- protected final Object mLock;
+ @UserIdInt protected final int mUserId;
+ public final Object mLock;
protected final String mTag = getClass().getSimpleName();
protected final M mMaster;
@@ -91,14 +90,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* <p><b>MUST</b> be overridden by subclasses that bind to an
* {@link com.android.internal.infra.AbstractRemoteService}.
*
- * @throws NameNotFoundException if the service does not exist.
- * @throws SecurityException if the service does not have the proper permissions to be bound to.
- * @throws UnsupportedOperationException if subclass binds to a remote service but does not
- * overrides it.
- *
* @return new {@link ServiceInfo},
+ * @throws NameNotFoundException if the service does not exist.
+ * @throws SecurityException if the service does not have the proper permissions to
+ * be bound to.
+ * @throws UnsupportedOperationException if subclass binds to a remote service but does not
+ * overrides it.
*/
- protected @NonNull ServiceInfo newServiceInfoLocked(
+ @NonNull protected ServiceInfo newServiceInfoLocked(
@SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
throws NameNotFoundException {
throw new UnsupportedOperationException("not overridden");
@@ -137,7 +136,6 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
- *
* @return whether the disabled state changed.
*/
@GuardedBy("mLock")
@@ -154,18 +152,48 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
updateIsSetupComplete(mUserId);
mDisabled = disabled;
- updateServiceInfoLocked();
+ if (mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
+ updateServiceInfoListLocked();
+ } else {
+ updateServiceInfoLocked();
+ }
return wasEnabled != isEnabledLocked();
}
/**
* Updates the internal reference to the service info, and returns the service's component.
*/
+ @GuardedBy("mLock")
protected final ComponentName updateServiceInfoLocked() {
- ComponentName serviceComponent = null;
- if (mMaster.mServiceNameResolver != null) {
- ServiceInfo serviceInfo = null;
+ ComponentName[] componentNames = updateServiceInfoListLocked();
+ return componentNames == null || componentNames.length == 0 ? null : componentNames[0];
+ }
+
+ /**
+ * Updates the internal reference to the service info, and returns the service's component.
+ */
+ @GuardedBy("mLock")
+ protected final ComponentName[] updateServiceInfoListLocked() {
+ if (mMaster.mServiceNameResolver == null) {
+ return null;
+ }
+ if (!mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
final String componentName = getComponentNameLocked();
+ return new ComponentName[] { getServiceComponent(componentName) };
+ }
+ final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
+ mUserId);
+ ComponentName[] serviceComponents = new ComponentName[componentNames.length];
+ for (int i = 0; i < componentNames.length; i++) {
+ serviceComponents[i] = getServiceComponent(componentNames[i]);
+ }
+ return serviceComponents;
+ }
+
+ private ComponentName getServiceComponent(String componentName) {
+ synchronized (mLock) {
+ ServiceInfo serviceInfo = null;
+ ComponentName serviceComponent = null;
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -196,14 +224,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
mServiceInfo = null;
}
+ return serviceComponent;
}
- return serviceComponent;
}
/**
* Gets the user associated with this service.
*/
- public final @UserIdInt int getUserId() {
+ @UserIdInt public final int getUserId() {
return mUserId;
}
@@ -229,15 +257,34 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets the current name of the service, which is either the default service or the
- * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
- protected final @Nullable String getComponentNameLocked() {
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
}
/**
+ * Gets the current name of the service, which is either the default service or the
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameForMultipleLocked(String serviceName) {
+ String[] services = mMaster.mServiceNameResolver.getServiceNameList(mUserId);
+ for (int i = 0; i < services.length; i++) {
+ if (serviceName.equals(services[i])) {
+ return services[i];
+ }
+ }
+ return null;
+ }
+
+ /**
* Checks whether the current service for the user was temporarily set.
*/
+ @GuardedBy("mLock")
public final boolean isTemporaryServiceSetLocked() {
return mMaster.mServiceNameResolver.isTemporary(mUserId);
}
@@ -245,6 +292,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Resets the temporary service implementation to the default component.
*/
+ @GuardedBy("mLock")
protected final void resetTemporaryServiceLocked() {
mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
}
@@ -268,6 +316,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
return mServiceInfo == null ? null : mServiceInfo.getComponentName();
}
}
+
/**
* Gets the name of the of the app this service binds to, or {@code null} if the service is
* disabled.
@@ -303,8 +352,10 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Removes the service from the main service's cache.
*/
- protected final void removeSelfFromCacheLocked() {
- mMaster.removeCachedServiceLocked(mUserId);
+ protected final void removeSelfFromCache() {
+ synchronized (mMaster.mLock) {
+ mMaster.removeCachedServiceListLocked(mUserId);
+ }
}
/**
@@ -327,6 +378,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
* Gets the target SDK level of the service this service binds to,
* or {@code 0} if the service is disabled.
*/
+ @GuardedBy("mLock")
public final int getTargedSdkLocked() {
return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
}
@@ -334,6 +386,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
/**
* Gets whether the device already finished setup.
*/
+ @GuardedBy("mLock")
protected final boolean isSetupCompletedLocked() {
return mSetupComplete;
}
@@ -348,19 +401,32 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.println(mUserId);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
- pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
+ pw.print(prefix);
+ pw.print("Service Label: ");
+ pw.println(getServiceLabelLocked());
+ pw.print(prefix);
+ pw.print("Target SDK: ");
+ pw.println(getTargedSdkLocked());
}
if (mMaster.mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mMaster.mServiceNameResolver.dumpShort(pw, mUserId);
+ pw.println();
}
- pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
- pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
+ pw.print(prefix);
+ pw.print("Disabled by UserManager: ");
+ pw.println(mDisabled);
+ pw.print(prefix);
+ pw.print("Setup complete: ");
+ pw.println(mSetupComplete);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service UID: ");
+ pw.print(prefix);
+ pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
pw.println();
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 35d59561fdeb..db2cb52d778e 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -15,6 +15,7 @@
*/
package com.android.server.infra;
+import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -33,6 +34,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -47,20 +49,20 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
/** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
- private final @NonNull Context mContext;
- private final @NonNull Object mLock = new Object();
- private final @StringRes int mResourceId;
- private @Nullable NameResolverListener mOnSetCallback;
-
+ @NonNull private final Context mContext;
+ @NonNull private final Object mLock = new Object();
+ @StringRes private final int mStringResourceId;
+ @ArrayRes private final int mArrayResourceId;
+ private final boolean mIsMultiple;
/**
- * Map of temporary service name set by {@link #setTemporaryService(int, String, int)},
+ * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
* keyed by {@code userId}.
*
- * <p>Typically used by Shell command and/or CTS tests.
+ * <p>Typically used by Shell command and/or CTS tests to configure temporary services if
+ * mIsMultiple is true.
*/
@GuardedBy("mLock")
- private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
-
+ private final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
/**
* Map of default services that have been disabled by
* {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
@@ -69,7 +71,7 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
-
+ @Nullable private NameResolverListener mOnSetCallback;
/**
* When the temporary service will expire (and reset back to the default).
*/
@@ -85,7 +87,22 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@StringRes int resourceId) {
mContext = context;
- mResourceId = resourceId;
+ mStringResourceId = resourceId;
+ mArrayResourceId = -1;
+ mIsMultiple = false;
+ }
+
+ public FrameworkResourcesServiceNameResolver(@NonNull Context context,
+ @ArrayRes int resourceId, boolean isMultiple) {
+ if (!isMultiple) {
+ throw new UnsupportedOperationException("Please use "
+ + "FrameworkResourcesServiceNameResolver(context, @StringRes int) constructor "
+ + "if single service mode is requested.");
+ }
+ mContext = context;
+ mStringResourceId = -1;
+ mArrayResourceId = resourceId;
+ mIsMultiple = true;
}
@Override
@@ -96,22 +113,31 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
@Override
+ public String getServiceName(@UserIdInt int userId) {
+ String[] serviceNames = getServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+ }
+
+ @Override
public String getDefaultServiceName(@UserIdInt int userId) {
- synchronized (mLock) {
- final String name = mContext.getString(mResourceId);
- return TextUtils.isEmpty(name) ? null : name;
- }
+ String[] serviceNames = getDefaultServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
}
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
@Override
- public String getServiceName(@UserIdInt int userId) {
+ public String[] getServiceNameList(int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
+ String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
// Always log it, as it should only be used on CTS or during development
- Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName
- + " for user " + userId);
- return temporaryName;
+ Slog.w(TAG, "getServiceName(): using temporary name "
+ + Arrays.toString(temporaryNames) + " for user " + userId);
+ return temporaryNames;
}
final boolean disabled = mDefaultServicesDisabled.get(userId);
if (disabled) {
@@ -120,22 +146,50 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
+ "user " + userId);
return null;
}
- return getDefaultServiceName(userId);
+ return getDefaultServiceNameList(userId);
+
+ }
+ }
+
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
+ @Override
+ public String[] getDefaultServiceNameList(int userId) {
+ synchronized (mLock) {
+ if (mIsMultiple) {
+ return mContext.getResources().getStringArray(mArrayResourceId);
+ } else {
+ final String name = mContext.getString(mStringResourceId);
+ return TextUtils.isEmpty(name) ? new String[0] : new String[] { name };
+ }
}
}
@Override
+ public boolean isConfiguredInMultipleMode() {
+ return mIsMultiple;
+ }
+
+ @Override
public boolean isTemporary(@UserIdInt int userId) {
synchronized (mLock) {
- return mTemporaryServiceNames.get(userId) != null;
+ return mTemporaryServiceNamesList.get(userId) != null;
}
}
@Override
public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
int durationMs) {
+ setTemporaryServices(userId, new String[]{componentName}, durationMs);
+ }
+
+ @Override
+ public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
synchronized (mLock) {
- mTemporaryServiceNames.put(userId, componentName);
+ mTemporaryServiceNamesList.put(userId, componentNames);
if (mTemporaryHandler == null) {
mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
@@ -155,8 +209,10 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
}
mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
- notifyTemporaryServiceNameChangedLocked(userId, componentName,
- /* isTemporary= */ true);
+ for (int i = 0; i < componentNames.length; i++) {
+ notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
+ /* isTemporary= */ true);
+ }
}
}
@@ -164,8 +220,8 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public void resetTemporaryService(@UserIdInt int userId) {
synchronized (mLock) {
Slog.i(TAG, "resetting temporary service for user " + userId + " from "
- + mTemporaryServiceNames.get(userId));
- mTemporaryServiceNames.remove(userId);
+ + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
+ mTemporaryServiceNamesList.remove(userId);
if (mTemporaryHandler != null) {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
mTemporaryHandler = null;
@@ -207,16 +263,21 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public String toString() {
- return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
+ synchronized (mLock) {
+ return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
+ }
}
// TODO(b/117779333): support proto
@Override
public void dumpShort(@NonNull PrintWriter pw) {
synchronized (mLock) {
- pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
- pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
- pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size());
+ pw.print("FrameworkResourcesServiceNamer: resId=");
+ pw.print(mStringResourceId);
+ pw.print(", numberTemps=");
+ pw.print(mTemporaryServiceNamesList.size());
+ pw.print(", enabledDefaults=");
+ pw.print(mDefaultServicesDisabled.size());
}
}
@@ -224,13 +285,17 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
@Override
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
- pw.print("tmpName="); pw.print(temporaryName);
+ final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
+ pw.print("tmpName=");
+ pw.print(Arrays.toString(temporaryNames));
final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
- pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
+ pw.print(" (expires in ");
+ TimeUtils.formatDuration(ttl, pw);
+ pw.print("), ");
}
- pw.print("defaultName="); pw.print(getDefaultServiceName(userId));
+ pw.print("defaultName=");
+ pw.print(getDefaultServiceName(userId));
final boolean disabled = mDefaultServicesDisabled.get(userId);
pw.println(disabled ? " (disabled)" : " (enabled)");
}
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
new file mode 100644
index 000000000000..0466d8a88053
--- /dev/null
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 655446
+
+include /core/java/android/service/cloudsearch/OWNERS
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index e20c45992e05..7d85fdb45266 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -34,7 +34,7 @@ public interface ServiceNameResolver {
/**
* Listener for name changes.
*/
- public interface NameResolverListener {
+ interface NameResolverListener {
/**
* The name change callback.
@@ -64,6 +64,30 @@ public interface ServiceNameResolver {
String getDefaultServiceName(@UserIdInt int userId);
/**
+ * Gets the default list of names of the services for the given user.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ @Nullable
+ default String[] getDefaultServiceNameList(@UserIdInt int userId) {
+ if (isConfiguredInMultipleMode()) {
+ throw new UnsupportedOperationException("getting default service list not supported");
+ } else {
+ return new String[] { getDefaultServiceName(userId) };
+ }
+ }
+
+ /**
+ * Returns whether the resolver is configured to connect to multiple backend services.
+ * The default return type is false.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ default boolean isConfiguredInMultipleMode() {
+ return false;
+ }
+
+ /**
* Gets the current name of the service for the given user
*
* @return either the temporary name (set by
@@ -76,6 +100,18 @@ public interface ServiceNameResolver {
}
/**
+ * Gets the current name of the service for the given user
+ *
+ * @return either the temporary name (set by
+ * {@link #setTemporaryService(int, String, int)}, or the
+ * {@link #getDefaultServiceName(int) default name}.
+ */
+ @Nullable
+ default String[] getServiceNameList(@UserIdInt int userId) {
+ return getDefaultServiceNameList(userId);
+ }
+
+ /**
* Checks whether the current service is temporary for the given user.
*/
default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
@@ -85,11 +121,11 @@ public interface ServiceNameResolver {
/**
* Temporarily sets the service implementation for the given user.
*
- * @param userId user handle
+ * @param userId user handle
* @param componentName name of the new component
- * @param durationMs how long the change will be valid (the service will be automatically reset
- * to the default component after this timeout expires).
- *
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
* @throws UnsupportedOperationException if not implemented.
*/
default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
@@ -98,10 +134,24 @@ public interface ServiceNameResolver {
}
/**
+ * Temporarily sets the service implementation for the given user.
+ *
+ * @param userId user handle
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ throw new UnsupportedOperationException("temporary user not supported");
+ }
+
+ /**
* Resets the temporary service implementation to the default component for the given user.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default void resetTemporaryService(@UserIdInt int userId) {
@@ -114,11 +164,11 @@ public interface ServiceNameResolver {
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @param userId user handle
+ * @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not
- * set. If the service enabled state is already that value, the command is ignored and this
- * method return {@code false}.
- *
+ * set. If the service enabled state is already that value, the command is
+ * ignored and this
+ * method return {@code false}.
* @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented.
*/
@@ -133,7 +183,6 @@ public interface ServiceNameResolver {
* with the test results.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default boolean isDefaultServiceEnabled(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c15242a45482..140a28f111e6 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -144,6 +144,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.OptionalInt;
/*
* Wraps the C++ InputManager and provides its callbacks.
@@ -2915,48 +2916,17 @@ public class InputManagerService extends IInputManager.Stub
// Native callback
@SuppressWarnings("unused")
- private void notifyWindowUnresponsive(IBinder token, String reason) {
- int gestureMonitorPid = -1;
- synchronized (mInputMonitors) {
- final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
- if (gestureMonitor != null) {
- gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
- }
- }
- if (gestureMonitorPid != -1) {
- mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason);
- return;
- }
- mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
- }
-
- // Native callback
- @SuppressWarnings("unused")
- private void notifyMonitorUnresponsive(int pid, String reason) {
- mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(pid, reason);
+ private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,
+ String reason) {
+ mWindowManagerCallbacks.notifyWindowUnresponsive(token,
+ isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
}
// Native callback
@SuppressWarnings("unused")
- private void notifyWindowResponsive(IBinder token) {
- int gestureMonitorPid = -1;
- synchronized (mInputMonitors) {
- final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
- if (gestureMonitor != null) {
- gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
- }
- }
- if (gestureMonitorPid != -1) {
- mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid);
- return;
- }
- mWindowManagerCallbacks.notifyWindowResponsive(token);
- }
-
- // Native callback
- @SuppressWarnings("unused")
- private void notifyMonitorResponsive(int pid) {
- mWindowManagerCallbacks.notifyGestureMonitorResponsive(pid);
+ private void notifyWindowResponsive(IBinder token, int pid, boolean isPidValid) {
+ mWindowManagerCallbacks.notifyWindowResponsive(token,
+ isPidValid ? OptionalInt.of(pid) : OptionalInt.empty());
}
// Native callback.
@@ -3329,34 +3299,22 @@ public class InputManagerService extends IInputManager.Stub
void notifyNoFocusedWindowAnr(InputApplicationHandle applicationHandle);
/**
- * Notify the window manager about a gesture monitor that is unresponsive.
- *
- * @param pid the pid of the gesture monitor process
- * @param reason the reason why this connection is unresponsive
- */
- void notifyGestureMonitorUnresponsive(int pid, @NonNull String reason);
-
- /**
* Notify the window manager about a window that is unresponsive.
*
* @param token the token that can be used to look up the window
+ * @param pid the pid of the window owner, if known
* @param reason the reason why this connection is unresponsive
*/
- void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull String reason);
-
- /**
- * Notify the window manager about a gesture monitor that has become responsive.
- *
- * @param pid the pid of the gesture monitor process
- */
- void notifyGestureMonitorResponsive(int pid);
+ void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
+ @NonNull String reason);
/**
* Notify the window manager about a window that has become responsive.
*
* @param token the token that can be used to look up the window
+ * @param pid the pid of the window owner, if known
*/
- void notifyWindowResponsive(@NonNull IBinder token);
+ void notifyWindowResponsive(@NonNull IBinder token, @NonNull OptionalInt pid);
/**
* This callback is invoked when an event first arrives to InputDispatcher and before it is
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index c42770555bab..176c08c8da29 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -78,10 +78,20 @@ public class LocaleManagerService extends SystemService {
Process.THREAD_PRIORITY_BACKGROUND);
broadcastHandlerThread.start();
+ SystemAppUpdateTracker systemAppUpdateTracker =
+ new SystemAppUpdateTracker(this);
+ broadcastHandlerThread.getThreadHandler().postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ systemAppUpdateTracker.init();
+ }
+ });
+
mBackupHelper = new LocaleManagerBackupHelper(this,
mPackageManagerInternal, broadcastHandlerThread);
- mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
+ systemAppUpdateTracker);
mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
UserHandle.ALL,
true);
@@ -246,7 +256,7 @@ public class LocaleManagerService extends SystemService {
* <p><b>Note:</b> This is can be used by installers to deal with cases such as
* language-based APK Splits.
*/
- private void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
+ void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
LocaleList locales) {
String installingPackageName = getInstallingPackageName(appPackageName);
if (installingPackageName != null) {
@@ -271,7 +281,7 @@ public class LocaleManagerService extends SystemService {
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- private static Intent createBaseIntent(String intentAction, String appPackageName,
+ static Intent createBaseIntent(String intentAction, String appPackageName,
LocaleList locales) {
return new Intent(intentAction)
.putExtra(Intent.EXTRA_PACKAGE_NAME, appPackageName)
@@ -406,7 +416,7 @@ public class LocaleManagerService extends SystemService {
}
@Nullable
- private String getInstallingPackageName(String packageName) {
+ String getInstallingPackageName(String packageName) {
try {
return mContext.getPackageManager()
.getInstallSourceInfo(packageName).getInstallingPackageName();
diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
index b459be768b9f..32080ef356ae 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
@@ -23,13 +23,22 @@ import com.android.internal.content.PackageMonitor;
*
* <p> These listeners forward the call to different aspects of locale service that
* handle the business logic.
- * <p> We're interested in package added, package data cleared and package removed events.
+ * <p> We're interested in the following events:
+ * <ul>
+ * <li> Package added
+ * <li> Package data cleared
+ * <li> Package removed
+ * <li> Package Updated
+ * </ul>
*/
final class LocaleManagerServicePackageMonitor extends PackageMonitor {
private LocaleManagerBackupHelper mBackupHelper;
+ private SystemAppUpdateTracker mSystemAppUpdateTracker;
- LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) {
+ LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper,
+ SystemAppUpdateTracker systemAppUpdateTracker) {
mBackupHelper = localeManagerBackupHelper;
+ mSystemAppUpdateTracker = systemAppUpdateTracker;
}
@Override
@@ -46,4 +55,9 @@ final class LocaleManagerServicePackageMonitor extends PackageMonitor {
public void onPackageRemoved(String packageName, int uid) {
mBackupHelper.onPackageRemoved();
}
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ mSystemAppUpdateTracker.onPackageUpdateFinished(packageName, uid);
+ }
}
diff --git a/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java b/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java
new file mode 100644
index 000000000000..d13b1f4d2845
--- /dev/null
+++ b/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static com.android.server.locales.LocaleManagerService.DEBUG;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Track if a system app is being updated for the first time after the user completed device setup.
+ *
+ * <p> The entire operation is being done on a background thread from {@link LocaleManagerService}.
+ * If it is the first time that a system app is being updated, then it fetches the app-specific
+ * locales and sends a broadcast to the newly set installer of the app. It maintains a file to store
+ * the name of the apps that have been updated.
+ */
+public class SystemAppUpdateTracker {
+ private static final String TAG = "SystemAppUpdateTracker";
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String SYSTEM_APPS_XML_TAG = "system_apps";
+
+ private final Context mContext;
+ private final LocaleManagerService mLocaleManagerService;
+ private final AtomicFile mUpdatedAppsFile;
+
+ // Lock used while writing to the file.
+ private final Object mFileLock = new Object();
+
+ // In-memory list of all the system apps that have been updated once after device setup.
+ // We do not need to store the userid->packages mapping because when updating a system app on
+ // one user updates for all users.
+ private final Set<String> mUpdatedApps = new HashSet<>();
+
+ SystemAppUpdateTracker(LocaleManagerService localeManagerService) {
+ this(localeManagerService.mContext, localeManagerService, new AtomicFile(
+ new File(Environment.getDataSystemDirectory(),
+ /* child = */ "locale_manager_service_updated_system_apps.xml")));
+ }
+
+ @VisibleForTesting
+ SystemAppUpdateTracker(Context context, LocaleManagerService localeManagerService,
+ AtomicFile file) {
+ mContext = context;
+ mLocaleManagerService = localeManagerService;
+ mUpdatedAppsFile = file;
+ }
+
+ /**
+ * Loads the info of updated system apps from the file.
+ *
+ * <p> Invoked once during device boot from {@link LocaleManagerService} by a background thread.
+ */
+ void init() {
+ if (DEBUG) {
+ Slog.d(TAG, "Loading the app info from storage. ");
+ }
+ loadUpdatedSystemApps();
+ }
+
+ /**
+ * Reads the XML stored in the {@link #mUpdatedAppsFile} and populates it in the in-memory list
+ * {@link #mUpdatedApps}.
+ */
+ private void loadUpdatedSystemApps() {
+ if (!mUpdatedAppsFile.getBaseFile().exists()) {
+ if (DEBUG) {
+ Slog.d(TAG, "loadUpdatedSystemApps: File does not exist.");
+ }
+ return;
+ }
+ InputStream updatedAppNamesInputStream = null;
+ try {
+ updatedAppNamesInputStream = mUpdatedAppsFile.openRead();
+ readFromXml(updatedAppNamesInputStream);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "loadUpdatedSystemApps: Could not parse storage file ", e);
+ } finally {
+ IoUtils.closeQuietly(updatedAppNamesInputStream);
+ }
+ }
+
+ /**
+ * Parses the update data from the serialized XML input stream.
+ */
+ private void readFromXml(InputStream updateInfoInputStream)
+ throws XmlPullParserException, IOException {
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(updateInfoInputStream, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, SYSTEM_APPS_XML_TAG);
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_NAME);
+ if (!TextUtils.isEmpty(packageName)) {
+ mUpdatedApps.add(packageName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends a broadcast to the newly set installer with app-locales if it is a system app being
+ * updated for the first time.
+ *
+ * <p><b>Note:</b> Invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageUpdateFinished} when a package updated.
+ */
+ void onPackageUpdateFinished(String packageName, int uid) {
+ try {
+ if ((!mUpdatedApps.contains(packageName)) && isUpdatedSystemApp(packageName)) {
+ // If a system app is updated, verify that it has an installer-on-record.
+ String installingPackageName = mLocaleManagerService.getInstallingPackageName(
+ packageName);
+ if (installingPackageName == null) {
+ // We want to broadcast the locales info to the installer.
+ // If this app does not have an installer then do nothing.
+ return;
+ }
+
+ try {
+ int userId = UserHandle.getUserId(uid);
+ // Fetch the app-specific locales.
+ // If non-empty then send the info to the installer.
+ LocaleList appLocales = mLocaleManagerService.getApplicationLocales(
+ packageName, userId);
+ if (!appLocales.isEmpty()) {
+ // The broadcast would be sent to the newly set installer of the
+ // updated system app.
+ mLocaleManagerService.notifyInstallerOfAppWhoseLocaleChanged(packageName,
+ userId, appLocales);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "onPackageUpdateFinished: Error in fetching app locales");
+ }
+ }
+ updateBroadcastedAppsList(packageName);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageUpdateFinished.", e);
+ }
+ }
+
+ /**
+ * Writes in-memory data {@link #mUpdatedApps} to the storage file in a synchronized manner.
+ */
+ private void updateBroadcastedAppsList(String packageName) {
+ synchronized (mFileLock) {
+ mUpdatedApps.add(packageName);
+ writeUpdatedAppsFileLocked();
+ }
+ }
+
+ private void writeUpdatedAppsFileLocked() {
+ FileOutputStream stream = null;
+ try {
+ stream = mUpdatedAppsFile.startWrite();
+ writeToXmlLocked(stream);
+ mUpdatedAppsFile.finishWrite(stream);
+ } catch (IOException e) {
+ mUpdatedAppsFile.failWrite(stream);
+ Slog.e(TAG, "Failed to persist the updated apps list", e);
+ }
+ }
+
+ /**
+ * Converts the list of updated app data into a serialized xml stream.
+ */
+ private void writeToXmlLocked(OutputStream stream) throws IOException {
+ final TypedXmlSerializer xml = Xml.newFastSerializer();
+ xml.setOutput(stream, StandardCharsets.UTF_8.name());
+ xml.startDocument(/* encoding= */ null, /* standalone= */ true);
+ xml.startTag(/* namespace= */ null, SYSTEM_APPS_XML_TAG);
+
+ for (String packageName : mUpdatedApps) {
+ xml.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ xml.attribute(/* namespace= */ null, ATTR_NAME, packageName);
+ xml.endTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ }
+
+ xml.endTag(null, SYSTEM_APPS_XML_TAG);
+ xml.endDocument();
+ }
+
+ private boolean isUpdatedSystemApp(String packageName) {
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "isUpdatedSystemApp: Package not found " + packageName);
+ }
+ }
+ if (appInfo == null) {
+ return false;
+ }
+ return (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
+ @VisibleForTesting
+ Set<String> getUpdatedApps() {
+ return mUpdatedApps;
+ }
+}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 5093f5dee55e..b6342a472023 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -19,7 +19,6 @@ package com.android.server.location.geofence;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import android.annotation.Nullable;
@@ -41,6 +40,7 @@ import android.stats.location.LocationStatsEnums;
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.injector.Injector;
@@ -396,7 +396,7 @@ public class GeofenceManager extends
protected boolean registerWithService(LocationRequest locationRequest,
Collection<GeofenceRegistration> registrations) {
getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest,
- DIRECT_EXECUTOR, this);
+ FgThread.getExecutor(), this);
return true;
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index acbee11f3b72..721ef1ed358f 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -201,18 +201,54 @@ public class LocationProviderManager extends
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
@Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
+ try {
+ mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
@Override
public void deliverOnFlushComplete(int requestCode) throws RemoteException {
- mListener.onFlushComplete(requestCode);
+ try {
+ mListener.onFlushComplete(requestCode);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
@Override
public void deliverOnProviderEnabledChanged(String provider, boolean enabled)
throws RemoteException {
- mListener.onProviderEnabledChanged(provider, enabled);
+ try {
+ mListener.onProviderEnabledChanged(provider, enabled);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
}
@@ -294,10 +330,23 @@ public class LocationProviderManager extends
throws RemoteException {
// ILocationCallback doesn't currently support completion callbacks
Preconditions.checkState(onCompleteCallback == null);
- if (locationResult != null) {
- mCallback.onLocation(locationResult.getLastLocation());
- } else {
- mCallback.onLocation(null);
+
+ try {
+ if (locationResult != null) {
+ mCallback.onLocation(locationResult.getLastLocation());
+ } else {
+ mCallback.onLocation(null);
+ }
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
}
}
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 9cb8a0105286..4f2680904fae 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -82,7 +82,7 @@ public class GroupHelper {
}
String combinedKey = generatePackageGroupKey(userId, sbn.getPackageName(), group);
boolean needsOngoingFlag = notifications.size() > 0;
- mCallback.updateAutogroupSummary(sbn.getKey(), needsOngoingFlag);
+ mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), needsOngoingFlag);
}
public void onNotificationUpdated(StatusBarNotification childSbn,
@@ -211,6 +211,6 @@ public class GroupHelper {
void removeAutoGroup(String key);
void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
void removeAutoGroupSummary(int user, String pkg);
- void updateAutogroupSummary(String key, boolean needsOngoingFlag);
+ void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a711b442c450..050cfeae80b9 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2587,19 +2587,11 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void updateAutogroupSummary(String key, boolean needsOngoingFlag) {
- String pkg;
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
- }
+ public void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag) {
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- if (r == null) return;
- updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
- r.getSbn().getPackageName(), needsOngoingFlag, isAppForeground);
+ updateAutobundledSummaryFlags(userId, pkg, needsOngoingFlag, isAppForeground);
}
}
});
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 30ac1b85e7c3..0c9855b2385d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2161,8 +2161,8 @@ public class ComputerEngine implements Computer {
private String[] getPackagesForUidInternal(int uid, int callingUid) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int appId = UserHandle.getAppId(uid);
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
@@ -2401,9 +2401,9 @@ public class ComputerEngine implements Computer {
}
public final boolean isCallerSameApp(String packageName, int uid) {
- if (Process.isSupplemental(uid)) {
+ if (Process.isSdkSandboxUid(uid)) {
return (packageName != null
- && packageName.equals(mService.getSupplementalProcessPackageName()));
+ && packageName.equals(mService.getSdkSandboxPackageName()));
}
AndroidPackage pkg = mPackages.get(packageName);
return pkg != null
@@ -2812,8 +2812,8 @@ public class ComputerEngine implements Computer {
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission");
} else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0
&& isCallerSystemUser
- && mUserManager.hasManagedProfile(UserHandle.USER_SYSTEM)) {
- // If the caller wants all packages and has a restricted profile associated with it,
+ && mUserManager.hasProfile(UserHandle.USER_SYSTEM)) {
+ // If the caller wants all packages and has a profile associated with it,
// then match all users. This is to make sure that launchers that need to access
//work
// profile apps don't start breaking. TODO: Remove this hack when launchers stop
@@ -4326,8 +4326,8 @@ public class ComputerEngine implements Computer {
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
@@ -4362,8 +4362,8 @@ public class ComputerEngine implements Computer {
final String[] names = new String[uids.length];
for (int i = uids.length - 1; i >= 0; i--) {
int uid = uids[i];
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingBase(appId);
@@ -4411,8 +4411,8 @@ public class ComputerEngine implements Computer {
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
@@ -4439,8 +4439,8 @@ public class ComputerEngine implements Computer {
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
@@ -4466,8 +4466,8 @@ public class ComputerEngine implements Computer {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingBase(appId);
@@ -5597,8 +5597,8 @@ public class ComputerEngine implements Computer {
@Override
public int getUidTargetSdkVersion(int uid) {
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int appId = UserHandle.getAppId(uid);
final SettingBase settingBase = mSettings.getSettingBase(appId);
@@ -5628,8 +5628,8 @@ public class ComputerEngine implements Computer {
@Nullable
@Override
public ArrayMap<String, ProcessInfo> getProcessesForUid(int uid) {
- if (Process.isSupplemental(uid)) {
- uid = getSupplementalProcessUid();
+ if (Process.isSdkSandboxUid(uid)) {
+ uid = getBaseSdkSandboxUid();
}
final int appId = UserHandle.getAppId(uid);
final SettingBase settingBase = mSettings.getSettingBase(appId);
@@ -5661,8 +5661,8 @@ public class ComputerEngine implements Computer {
}
}
- private int getSupplementalProcessUid() {
- return getPackage(mService.getSupplementalProcessPackageName()).getUid();
+ private int getBaseSdkSandboxUid() {
+ return getPackage(mService.getSdkSandboxPackageName()).getUid();
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 53eb9cf7d9fe..c8326931f6fd 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -460,7 +460,8 @@ final class DexOptHelper {
| DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
| DexoptOptions.DEXOPT_BOOT_COMPLETE
| (force ? DexoptOptions.DEXOPT_FORCE : 0);
- return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
+ return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
+ compilerFilter, null /* splitName */, flags));
}
// Sort apps by importance for dexopt ordering. Important apps are given
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 54c201945be6..ceab92577bb3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2893,9 +2893,13 @@ final class InstallPackageHelper {
}
}
- final boolean deferInstallObserver = succeeded && update && !killApp;
+ final boolean deferInstallObserver = succeeded && update;
if (deferInstallObserver) {
- mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+ if (killApp) {
+ mPm.scheduleDeferredPendingKillInstallObserver(res, installObserver);
+ } else {
+ mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+ }
} else {
mPm.notifyInstallObserver(res, installObserver);
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index b028a2cef2a5..e8faca9765f8 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -24,6 +24,7 @@ import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
+import static com.android.server.pm.PackageManagerService.DEFERRED_PENDING_KILL_INSTALL_OBSERVER;
import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
@@ -126,10 +127,12 @@ final class PackageHandler extends Handler {
}
}
} break;
- case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
- String packageName = (String) msg.obj;
+ case DEFERRED_NO_KILL_INSTALL_OBSERVER:
+ case DEFERRED_PENDING_KILL_INSTALL_OBSERVER: {
+ final String packageName = (String) msg.obj;
if (packageName != null) {
- mPm.notifyInstallObserver(packageName);
+ final boolean killApp = msg.what == DEFERRED_PENDING_KILL_INSTALL_OBSERVER;
+ mPm.notifyInstallObserver(packageName, killApp);
}
} break;
case WRITE_SETTINGS: {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c05faf14cae4..6fbad24d58c0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -247,8 +247,8 @@ import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import com.android.server.storage.DeviceStorageMonitorInternal;
-import com.android.server.supplementalprocess.SupplementalProcessManagerLocal;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
@@ -840,6 +840,9 @@ public class PackageManagerService extends IPackageManager.Stub
private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
+ private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+ mPendingKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
+
// Internal interface for permission manager
final PermissionManagerServiceInternal mPermissionManager;
@@ -887,9 +890,11 @@ public class PackageManagerService extends IPackageManager.Stub
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DOMAIN_VERIFICATION = 27;
static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
+ static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
+ private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -934,7 +939,7 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
final @Nullable String mAmbientContextDetectionPackage;
- private final @NonNull String mRequiredSupplementalProcessPackage;
+ private final @NonNull String mRequiredSdkSandboxPackage;
@GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1166,13 +1171,14 @@ public class PackageManagerService extends IPackageManager.Stub
Computer computer = snapshotComputer();
ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
for (int index = 0; index < packagesToNotify.size(); index++) {
- notifyInstallObserver(packagesToNotify.valueAt(index));
+ notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */);
}
}
- void notifyInstallObserver(String packageName) {
- Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
- mNoKillInstallObservers.remove(packageName);
+ void notifyInstallObserver(String packageName, boolean killApp) {
+ final Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
+ killApp ? mPendingKillInstallObservers.remove(packageName)
+ : mNoKillInstallObservers.remove(packageName);
if (pair != null) {
notifyInstallObserver(pair.first, pair.second);
@@ -1211,6 +1217,15 @@ public class PackageManagerService extends IPackageManager.Stub
delay ? getPruneUnusedSharedLibrariesDelay() : 0);
}
+ void scheduleDeferredPendingKillInstallObserver(PackageInstalledInfo info,
+ IPackageInstallObserver2 observer) {
+ final String packageName = info.mPkg.getPackageName();
+ mPendingKillInstallObservers.put(packageName, Pair.create(info, observer));
+ final Message message = mHandler.obtainMessage(DEFERRED_PENDING_KILL_INSTALL_OBSERVER,
+ packageName);
+ mHandler.sendMessageDelayed(message, DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS);
+ }
+
private static long getPruneUnusedSharedLibrariesDelay() {
return SystemProperties.getLong("debug.pm.prune_unused_shared_libraries_delay",
PRUNE_UNUSED_SHARED_LIBRARIES_DELAY);
@@ -1667,7 +1682,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
- mRequiredSupplementalProcessPackage = testParams.requiredSupplementalProcessPackage;
+ mRequiredSdkSandboxPackage = testParams.requiredSdkSandboxPackage;
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
@@ -2055,8 +2070,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot();
-
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
// Note that we do *not* clear the application profiles. These remain valid
@@ -2077,6 +2090,9 @@ public class PackageManagerService extends IPackageManager.Stub
ver.fingerprint = PackagePartitions.FINGERPRINT;
}
+ // Defer the app data fixup until we are done with app data clearing above.
+ mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot();
+
// Legacy existing (installed before Q) non-system apps to hide
// their icons in launcher.
if (!mOnlyCore && mIsPreQUpgrade) {
@@ -2141,8 +2157,8 @@ public class PackageManagerService extends IPackageManager.Stub
getPackageInfo(mRequiredPermissionControllerPackage, 0,
UserHandle.USER_SYSTEM).getLongVersionCode());
- // Resolve the supplemental process
- mRequiredSupplementalProcessPackage = getRequiredSupplementalProcessPackageName();
+ // Resolve the sdk sandbox package
+ mRequiredSdkSandboxPackage = getRequiredSdkSandboxPackageName();
// Initialize InstantAppRegistry's Instant App list for all users.
for (AndroidPackage pkg : mPackages.values()) {
@@ -2557,8 +2573,9 @@ public class PackageManagerService extends IPackageManager.Stub
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
- if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)) {
- Slog.wtf(TAG, "Package Manager Crash", e);
+ if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)
+ && !(e instanceof ParcelableException)) {
+ Slog.wtf(TAG, "Package Manager Unexpected Exception", e);
}
throw e;
}
@@ -3143,8 +3160,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public String getSupplementalProcessPackageName() {
- return mRequiredSupplementalProcessPackage;
+ public String getSdkSandboxPackageName() {
+ return mRequiredSdkSandboxPackage;
}
String getPackageInstallerPackageName() {
@@ -5458,8 +5475,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private @NonNull String getRequiredSupplementalProcessPackageName() {
- final Intent intent = new Intent(SupplementalProcessManagerLocal.SERVICE_INTERFACE);
+ private @NonNull String getRequiredSdkSandboxPackageName() {
+ final Intent intent = new Intent(SdkSandboxManagerLocal.SERVICE_INTERFACE);
final List<ResolveInfo> matches = queryIntentServicesInternal(
intent,
@@ -5471,7 +5488,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else {
- throw new RuntimeException("There should exactly one supplemental process; found "
+ throw new RuntimeException("There should exactly one sdk sandbox package; found "
+ matches.size() + ": matches=" + matches);
}
}
@@ -6675,6 +6692,11 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public IPackageInstaller getPackageInstaller() {
+ // Return installer service for internal calls.
+ if (PackageManagerServiceUtils.isSystemOrRoot()) {
+ return mInstallerService;
+ }
+ // Return null for InstantApps.
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
@@ -7393,6 +7415,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public void onPackageProcessKilledForUninstall(String packageName) {
+ mHandler.post(() -> PackageManagerService.this.notifyInstallObserver(packageName,
+ true /* killApp */));
+ }
+
+ @Override
public SparseArray<String> getAppsWithSharedUserIds() {
return mComputer.getAppsWithSharedUserIds();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index d12c826b8d40..5bdda0b3c1d8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -89,7 +89,7 @@ public final class PackageManagerServiceTestParams {
public @Nullable String defaultTextClassifierPackage;
public @Nullable String systemTextClassifierPackage;
public @Nullable String overlayConfigSignaturePackage;
- public @NonNull String requiredSupplementalProcessPackage;
+ public @NonNull String requiredSdkSandboxPackage;
public ViewCompiler viewCompiler;
public @Nullable String retailDemoPackage;
public @Nullable String recentsPackage;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a9471cf57070..8d3fbf7fc679 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1250,6 +1250,14 @@ public class PackageManagerServiceUtils {
}
/**
+ * Check if the Binder caller is system UID or root's UID.
+ */
+ public static boolean isSystemOrRoot() {
+ final int uid = Binder.getCallingUid();
+ return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID;
+ }
+
+ /**
* Enforces that only the system UID or root's UID can call a method exposed
* via Binder.
*
@@ -1257,8 +1265,7 @@ public class PackageManagerServiceUtils {
* @throws SecurityException if the caller is not system or root
*/
public static void enforceSystemOrRoot(String message) {
- final int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
+ if (!isSystemOrRoot()) {
throw new SecurityException(message);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 394c8fb73156..3e5ab1e058d9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3906,13 +3906,17 @@ public final class Settings implements Watchable, Snappable {
} else if (tagName.equals(TAG_PERMISSIONS)) {
final LegacyPermissionState legacyState;
if (packageSetting.hasSharedUser()) {
- legacyState = getSettingLPr(
- packageSetting.getSharedUserAppId()).getLegacyPermissionState();
+ final SettingBase sharedUserSettings = getSettingLPr(
+ packageSetting.getSharedUserAppId());
+ legacyState = sharedUserSettings != null
+ ? sharedUserSettings.getLegacyPermissionState() : null;
} else {
legacyState = packageSetting.getLegacyPermissionState();
}
- readInstallPermissionsLPr(parser, legacyState, users);
- packageSetting.setInstallPermissionsFixed(true);
+ if (legacyState != null) {
+ readInstallPermissionsLPr(parser, legacyState, users);
+ packageSetting.setInstallPermissionsFixed(true);
+ }
} else if (tagName.equals("proper-signing-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
Integer refCt = mKeySetRefs.get(id);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b7c55c57c6ce..fed214fd5ab0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -6263,11 +6263,11 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
- * Checks if the given user has a managed profile associated with it.
+ * Checks if the given user has a profile associated with it.
* @param userId The parent user
* @return
*/
- boolean hasManagedProfile(@UserIdInt int userId) {
+ boolean hasProfile(@UserIdInt int userId) {
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
final int userSize = mUsers.size();
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 49553f4c91f4..36633cc635e7 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1021,7 +1021,8 @@ final class DefaultPermissionGrantPolicy {
}
for (String packageName : packageNames) {
grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
- PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS);
+ PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java b/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
index d3c8c7b57df2..a0bb8dc48ecf 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
@@ -268,6 +268,9 @@ public interface AndroidPackageApi {
List<FeatureGroupInfo> getFeatureGroups();
@NonNull
+ List<String> getImplicitPermissions();
+
+ @NonNull
List<ParsedInstrumentation> getInstrumentations();
long getLongVersionCode();
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index c637c6764092..f2ce0d4c49d3 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -34,6 +34,7 @@ import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
@@ -150,6 +151,7 @@ public final class PermissionPolicyService extends SystemService {
private Context mContext;
private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManager;
+ private final KeyguardManager mKeyguardManager;
private final PackageManager mPackageManager;
public PermissionPolicyService(@NonNull Context context) {
@@ -157,6 +159,7 @@ public final class PermissionPolicyService extends SystemService {
mContext = context;
mPackageManager = context.getPackageManager();
+ mKeyguardManager = context.getSystemService(KeyguardManager.class);
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
}
@@ -1046,12 +1049,21 @@ public final class PermissionPolicyService extends SystemService {
}
@Override
- public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
- super.onActivityLaunched(taskInfo, activityInfo);
- clearNotificationReviewFlagsIfNeeded(activityInfo.packageName,
- UserHandle.of(taskInfo.userId));
- showNotificationPromptIfNeeded(activityInfo.packageName,
- taskInfo.userId, taskInfo.taskId);
+ public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
+ ActivityInterceptorInfo info) {
+ super.onActivityLaunched(taskInfo, activityInfo, info);
+ if (!shouldShowNotificationDialogOrClearFlags(info.intent,
+ info.checkedOptions)) {
+ return;
+ }
+ UserHandle user = UserHandle.of(taskInfo.userId);
+ if (CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
+ activityInfo.packageName, user)) {
+ clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, user);
+ } else {
+ showNotificationPromptIfNeeded(activityInfo.packageName,
+ taskInfo.userId, taskInfo.taskId);
+ }
}
};
@@ -1092,10 +1104,28 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId);
}
+ /**
+ * Determine if we should show a notification dialog, or clear the REVIEW_REQUIRED flag,
+ * from a particular package for a particular intent. Returns true if:
+ * 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
+ * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
+ */
+ private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
+ ActivityOptions options) {
+ if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
+ return true;
+ }
+
+ return Intent.ACTION_MAIN.equals(intent.getAction())
+ && intent.getCategories() != null
+ && (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
+ || intent.getCategories().contains(Intent.CATEGORY_LEANBACK_LAUNCHER)
+ || intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER));
+ }
+
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
- if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user)
- || ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
- & FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) {
+ if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
+ & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
return;
}
try {
@@ -1210,8 +1240,8 @@ public final class PermissionPolicyService extends SystemService {
}
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
- || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
- pkg.getPackageName(), user)) {
+ || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
+ || mKeyguardManager.isKeyguardLocked()) {
return false;
}
@@ -1220,7 +1250,7 @@ public final class PermissionPolicyService extends SystemService {
mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
}
boolean hasCreatedNotificationChannels = mNotificationManager
- .getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0;
+ .getNumNotificationChannelsForPackage(pkgName, uid, true) > 0;
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bd58472f2d2c..914e5eccd192 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1176,14 +1176,14 @@ public final class PowerManagerService extends SystemService
@Override
public void onBootPhase(int phase) {
- synchronized (mLock) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- systemReady();
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ systemReady();
- } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- incrementBootCount();
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ incrementBootCount();
- } else if (phase == PHASE_BOOT_COMPLETED) {
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ synchronized (mLock) {
final long now = mClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index b03db66fde94..dcfb8b5e7b33 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -315,15 +315,15 @@ public final class ShutdownThread extends Thread {
com.android.internal.R.string.reboot_to_update_reboot));
}
} else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
- if (showSysuiReboot()) {
- return null;
- } else if (RescueParty.isAttemptingFactoryReset()) {
+ if (RescueParty.isAttemptingFactoryReset()) {
// We're not actually doing a factory reset yet; we're rebooting
// to ask the user if they'd like to reset, so give them a less
// scary dialog message.
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
+ } else if (showSysuiReboot()) {
+ return null;
} else {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 2491565dd376..8755662ac813 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -27,6 +27,7 @@ import android.os.IHintSession;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -51,8 +52,13 @@ public final class HintManagerService extends SystemService {
private static final boolean DEBUG = false;
@VisibleForTesting final long mHintSessionPreferredRate;
+ // Multi-levle map storing all active AppHintSessions.
+ // First level is keyed by the UID of the client process creating the session.
+ // Second level is keyed by an IBinder passed from client process. This is used to observe
+ // when the process exits. The client generally uses the same IBinder object across multiple
+ // sessions, so the value is a set of AppHintSessions.
@GuardedBy("mLock")
- private final ArrayMap<Integer, ArrayMap<IBinder, AppHintSession>> mActiveSessions;
+ private final ArrayMap<Integer, ArrayMap<IBinder, ArraySet<AppHintSession>>> mActiveSessions;
/** Lock to protect HAL handles and listen list. */
private final Object mLock = new Object();
@@ -201,13 +207,16 @@ public final class HintManagerService extends SystemService {
public void onUidGone(int uid, boolean disabled) {
FgThread.getHandler().post(() -> {
synchronized (mLock) {
- ArrayMap<IBinder, AppHintSession> tokenMap = mActiveSessions.get(uid);
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid);
if (tokenMap == null) {
return;
}
for (int i = tokenMap.size() - 1; i >= 0; i--) {
// Will remove the session from tokenMap
- tokenMap.valueAt(i).close();
+ ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(i);
+ for (int j = sessionSet.size() - 1; j >= 0; j--) {
+ sessionSet.valueAt(j).close();
+ }
}
mProcStatesCache.delete(uid);
}
@@ -231,12 +240,14 @@ public final class HintManagerService extends SystemService {
FgThread.getHandler().post(() -> {
synchronized (mLock) {
mProcStatesCache.put(uid, procState);
- ArrayMap<IBinder, AppHintSession> tokenMap = mActiveSessions.get(uid);
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid);
if (tokenMap == null) {
return;
}
- for (AppHintSession s : tokenMap.values()) {
- s.onProcStateChanged();
+ for (ArraySet<AppHintSession> sessionSet : tokenMap.values()) {
+ for (AppHintSession s : sessionSet) {
+ s.onProcStateChanged();
+ }
}
}
});
@@ -305,17 +316,25 @@ public final class HintManagerService extends SystemService {
long halSessionPtr = mNativeWrapper.halCreateHintSession(callingTgid, callingUid,
tids, durationNanos);
- if (halSessionPtr == 0) return null;
+ if (halSessionPtr == 0) {
+ return null;
+ }
AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token,
halSessionPtr, durationNanos);
synchronized (mLock) {
- ArrayMap<IBinder, AppHintSession> tokenMap = mActiveSessions.get(callingUid);
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
+ mActiveSessions.get(callingUid);
if (tokenMap == null) {
tokenMap = new ArrayMap<>(1);
mActiveSessions.put(callingUid, tokenMap);
}
- tokenMap.put(token, hs);
+ ArraySet<AppHintSession> sessionSet = tokenMap.get(token);
+ if (sessionSet == null) {
+ sessionSet = new ArraySet<>(1);
+ tokenMap.put(token, sessionSet);
+ }
+ sessionSet.add(hs);
return hs;
}
} finally {
@@ -339,10 +358,14 @@ public final class HintManagerService extends SystemService {
pw.println("Active Sessions:");
for (int i = 0; i < mActiveSessions.size(); i++) {
pw.println("Uid " + mActiveSessions.keyAt(i).toString() + ":");
- ArrayMap<IBinder, AppHintSession> tokenMap = mActiveSessions.valueAt(i);
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
+ mActiveSessions.valueAt(i);
for (int j = 0; j < tokenMap.size(); j++) {
- pw.println(" Session " + j + ":");
- tokenMap.valueAt(j).dump(pw, " ");
+ ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(j);
+ for (int k = 0; k < sessionSet.size(); ++k) {
+ pw.println(" Session:");
+ sessionSet.valueAt(k).dump(pw, " ");
+ }
}
}
}
@@ -432,11 +455,18 @@ public final class HintManagerService extends SystemService {
mNativeWrapper.halCloseHintSession(mHalSessionPtr);
mHalSessionPtr = 0;
mToken.unlinkToDeath(this, 0);
- ArrayMap<IBinder, AppHintSession> tokenMap = mActiveSessions.get(mUid);
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
if (tokenMap == null) {
- Slogf.w(TAG, "UID %d is note present in active session map", mUid);
+ Slogf.w(TAG, "UID %d is not present in active session map", mUid);
+ return;
+ }
+ ArraySet<AppHintSession> sessionSet = tokenMap.get(mToken);
+ if (sessionSet == null) {
+ Slogf.w(TAG, "Token %s is not present in token map", mToken.toString());
+ return;
}
- tokenMap.remove(mToken);
+ sessionSet.remove(this);
+ if (sessionSet.isEmpty()) tokenMap.remove(mToken);
if (tokenMap.isEmpty()) mActiveSessions.remove(mUid);
}
}
diff --git a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
new file mode 100644
index 000000000000..3543e9319a88
--- /dev/null
+++ b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.framework.protobuf.ByteString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1Set;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parsed {@link X509Certificate} attestation extension values for Android Keystore attestations.
+ *
+ * Pull fields out of the top-level sequence. A full description of this structure is at
+ * https://source.android.com/security/keystore/attestation.
+ *
+ * If a value is null or empty, then it was not set/found in the extension values.
+ *
+ */
+class AndroidKeystoreAttestationVerificationAttributes {
+ // The OID for the extension Android Keymaster puts into device-generated certificates.
+ private static final String ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID =
+ "1.3.6.1.4.1.11129.2.1.17";
+
+ // ASN.1 sequence index values for the Android Keymaster extension.
+ private static final int ATTESTATION_VERSION_INDEX = 0;
+ private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+ private static final int KEYMASTER_VERSION_INDEX = 2;
+ private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+ private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+ private static final int KEYMASTER_UNIQUE_ID_INDEX = 5;
+ private static final int SW_ENFORCED_INDEX = 6;
+ private static final int HW_ENFORCED_INDEX = 7;
+ private static final int VERIFIED_BOOT_KEY_INDEX = 0;
+ private static final int VERIFIED_BOOT_LOCKED_INDEX = 1;
+ private static final int VERIFIED_BOOT_STATE_INDEX = 2;
+ private static final int VERIFIED_BOOT_HASH_INDEX = 3;
+
+ // ASN.1 sequence index values for the Android Keystore application id.
+ private static final int PACKAGE_INFO_SET_INDEX = 0;
+ private static final int PACKAGE_SIGNATURE_SET_INDEX = 1;
+ private static final int PACKAGE_INFO_NAME_INDEX = 0;
+ private static final int PACKAGE_INFO_VERSION_INDEX = 1;
+
+ // See these AOSP files: hardware/libhardware/include/hardware/hw_auth_token.h
+ private static final int HW_AUTH_NONE = 0;
+
+ // Some keymaster constants. See this AOSP file:
+ // hardware/libhardware/include/hardware/keymaster_defs.h
+ private static final int KM_TAG_NO_AUTH_REQUIRED = 503;
+ private static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = 509;
+ private static final int KM_TAG_ALL_APPLICATIONS = 600;
+ private static final int KM_TAG_ROOT_OF_TRUST = 704;
+ private static final int KM_TAG_OS_VERSION = 705;
+ private static final int KM_TAG_OS_PATCHLEVEL = 706;
+ private static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
+ private static final int KM_TAG_ATTESTATION_ID_BRAND = 710;
+ private static final int KM_TAG_ATTESTATION_ID_DEVICE = 711;
+ private static final int KM_TAG_ATTESTATION_ID_PRODUCT = 712;
+ private static final int KM_TAG_VENDOR_PATCHLEVEL = 718;
+ private static final int KM_TAG_BOOT_PATCHLEVEL = 719;
+
+ private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
+ private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
+ private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
+ private static final int KM_VERIFIED_BOOT_STATE_VERIFIED = 0;
+ private static final int KM_VERIFIED_BOOT_STATE_SELF_SIGNED = 1;
+ private static final int KM_VERIFIED_BOOT_STATE_UNVERIFIED = 2;
+ private static final int KM_VERIFIED_BOOT_STATE_FAILED = 3;
+
+ private Integer mAttestationVersion = null;
+ private SecurityLevel mAttestationSecurityLevel = null;
+ private boolean mAttestationHardwareBacked = false;
+ private Integer mKeymasterVersion = null;
+ private SecurityLevel mKeymasterSecurityLevel = null;
+ private boolean mKeymasterHardwareBacked = false;
+ private ByteString mAttestationChallenge = null;
+ private ByteString mKeymasterUniqueId = null;
+ private String mDeviceBrand = null;
+ private String mDeviceName = null;
+ private String mDeviceProductName = null;
+ private boolean mKeyAllowedForAllApplications = false;
+ private Integer mKeyAuthenticatorType = null;
+ private Integer mKeyBootPatchLevel = null;
+ private Integer mKeyOsPatchLevel = null;
+ private Integer mKeyOsVersion = null;
+ private Integer mKeyVendorPatchLevel = null;
+ private Boolean mKeyRequiresUnlockedDevice = null;
+ private ByteString mVerifiedBootHash = null;
+ private ByteString mVerifiedBootKey = null;
+ private Boolean mVerifiedBootLocked = null;
+ private VerifiedBootState mVerifiedBootState = null;
+ private Map<String, Long> mApplicationPackageNameVersion = null;
+ private List<ByteString> mApplicationCertificateDigests = null;
+
+ enum VerifiedBootState {
+ VERIFIED,
+ SELF_SIGNED,
+ UNVERIFIED,
+ FAILED
+ }
+
+ enum SecurityLevel {
+ SOFTWARE,
+ TRUSTED_ENVIRONMENT,
+ STRONG_BOX
+ }
+
+ /**
+ * Extracts attestation extension properties from {@link X509Certificate}
+ * and returns a {@link AndroidKeystoreAttestationVerificationAttributes} that encapsulates the
+ * properties.
+ */
+ @NonNull
+ static AndroidKeystoreAttestationVerificationAttributes fromCertificate(
+ @NonNull X509Certificate x509Certificate)
+ throws Exception {
+ return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate);
+ }
+
+ int getAttestationVersion() {
+ return mAttestationVersion;
+ }
+
+ @Nullable
+ SecurityLevel getAttestationSecurityLevel() {
+ return mAttestationSecurityLevel;
+ }
+
+ boolean isAttestationHardwareBacked() {
+ return mAttestationHardwareBacked;
+ }
+
+ int getKeymasterVersion() {
+ return mKeymasterVersion;
+ }
+
+ @Nullable
+ SecurityLevel getKeymasterSecurityLevel() {
+ return mKeymasterSecurityLevel;
+ }
+
+ boolean isKeymasterHardwareBacked() {
+ return mKeymasterHardwareBacked;
+ }
+
+ @Nullable
+ ByteString getAttestationChallenge() {
+ return mAttestationChallenge;
+ }
+
+ @Nullable
+ ByteString getKeymasterUniqueId() {
+ return mKeymasterUniqueId;
+ }
+
+ @Nullable
+ String getDeviceBrand() {
+ return mDeviceBrand;
+ }
+
+ @Nullable
+ String getDeviceName() {
+ return mDeviceName;
+ }
+
+ @Nullable
+ String getDeviceProductName() {
+ return mDeviceProductName;
+ }
+
+ boolean isKeyAllowedForAllApplications() {
+ return mKeyAllowedForAllApplications;
+ }
+
+ int getKeyAuthenticatorType() {
+ if (mKeyAuthenticatorType == null) {
+ throw new IllegalStateException("KeyAuthenticatorType is not set.");
+ }
+ return mKeyAuthenticatorType;
+ }
+
+ int getKeyBootPatchLevel() {
+ if (mKeyBootPatchLevel == null) {
+ throw new IllegalStateException("KeyBootPatchLevel is not set.");
+ }
+ return mKeyBootPatchLevel;
+ }
+
+ int getKeyOsPatchLevel() {
+ if (mKeyOsPatchLevel == null) {
+ throw new IllegalStateException("KeyOsPatchLevel is not set.");
+ }
+ return mKeyOsPatchLevel;
+ }
+
+ int getKeyVendorPatchLevel() {
+ if (mKeyVendorPatchLevel == null) {
+ throw new IllegalStateException("KeyVendorPatchLevel is not set.");
+ }
+ return mKeyVendorPatchLevel;
+ }
+
+ int getKeyOsVersion() {
+ if (mKeyOsVersion == null) {
+ throw new IllegalStateException("KeyOsVersion is not set.");
+ }
+ return mKeyOsVersion;
+ }
+
+ boolean isKeyRequiresUnlockedDevice() {
+ if (mKeyRequiresUnlockedDevice == null) {
+ throw new IllegalStateException("KeyRequiresUnlockedDevice is not set.");
+ }
+ return mKeyRequiresUnlockedDevice;
+ }
+
+ @Nullable
+ ByteString getVerifiedBootHash() {
+ return mVerifiedBootHash;
+ }
+
+ @Nullable
+ ByteString getVerifiedBootKey() {
+ return mVerifiedBootKey;
+ }
+
+ boolean isVerifiedBootLocked() {
+ if (mVerifiedBootLocked == null) {
+ throw new IllegalStateException("VerifiedBootLocked is not set.");
+ }
+ return mVerifiedBootLocked;
+ }
+
+ @Nullable
+ VerifiedBootState getVerifiedBootState() {
+ return mVerifiedBootState;
+ }
+
+ @Nullable
+ Map<String, Long> getApplicationPackageNameVersion() {
+ return Collections.unmodifiableMap(mApplicationPackageNameVersion);
+ }
+
+ @Nullable
+ List<ByteString> getApplicationCertificateDigests() {
+ return Collections.unmodifiableList(mApplicationCertificateDigests);
+ }
+
+ private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate)
+ throws Exception {
+ Certificate certificate = Certificate.getInstance(
+ new ASN1InputStream(x509Certificate.getEncoded()).readObject());
+ ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
+ .getExtensionParsedValue(
+ new ASN1ObjectIdentifier(ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID));
+ if (keyAttributes == null) {
+ throw new CertificateEncodingException(
+ "No attestation extension found in certificate.");
+ }
+ this.mAttestationVersion = getIntegerFromAsn1(
+ keyAttributes.getObjectAt(ATTESTATION_VERSION_INDEX));
+ this.mAttestationSecurityLevel = getSecurityLevelEnum(
+ keyAttributes.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
+ this.mAttestationHardwareBacked =
+ this.mAttestationSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
+ this.mAttestationChallenge = getOctetsFromAsn1(
+ keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
+ this.mKeymasterVersion = getIntegerFromAsn1(
+ keyAttributes.getObjectAt(KEYMASTER_VERSION_INDEX));
+ this.mKeymasterUniqueId = getOctetsFromAsn1(
+ keyAttributes.getObjectAt(KEYMASTER_UNIQUE_ID_INDEX));
+ this.mKeymasterSecurityLevel = getSecurityLevelEnum(
+ keyAttributes.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
+ this.mKeymasterHardwareBacked =
+ this.mKeymasterSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
+
+ ASN1Encodable[] softwareEnforced = ((ASN1Sequence)
+ keyAttributes.getObjectAt(SW_ENFORCED_INDEX)).toArray();
+ for (ASN1Encodable entry : softwareEnforced) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ switch (taggedEntry.getTagNo()) {
+ case KM_TAG_ATTESTATION_APPLICATION_ID:
+ parseAttestationApplicationId(
+ getOctetsFromAsn1(taggedEntry.getObject()).toByteArray());
+ break;
+ case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
+ this.mKeyRequiresUnlockedDevice = getBoolFromAsn1(taggedEntry.getObject());
+ break;
+ default:
+ break;
+ }
+ }
+
+ ASN1Encodable[] hardwareEnforced = ((ASN1Sequence)
+ keyAttributes.getObjectAt(HW_ENFORCED_INDEX)).toArray();
+ for (ASN1Encodable entry : hardwareEnforced) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ switch (taggedEntry.getTagNo()) {
+ case KM_TAG_NO_AUTH_REQUIRED:
+ this.mKeyAuthenticatorType = HW_AUTH_NONE;
+ break;
+ case KM_TAG_ALL_APPLICATIONS:
+ this.mKeyAllowedForAllApplications = true;
+ break;
+ case KM_TAG_ROOT_OF_TRUST:
+ ASN1Sequence rootOfTrust = (ASN1Sequence) taggedEntry.getObject();
+ this.mVerifiedBootKey =
+ getOctetsFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_KEY_INDEX));
+ this.mVerifiedBootLocked =
+ getBoolFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_LOCKED_INDEX));
+ this.mVerifiedBootState =
+ getVerifiedBootStateEnum(
+ rootOfTrust.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
+ // The verified boot hash was added in structure version 3 (Keymaster 4.0).
+ if (mAttestationVersion >= 3) {
+ this.mVerifiedBootHash =
+ getOctetsFromAsn1(
+ rootOfTrust.getObjectAt(VERIFIED_BOOT_HASH_INDEX));
+ }
+ break;
+ case KM_TAG_OS_VERSION:
+ this.mKeyOsVersion = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_OS_PATCHLEVEL:
+ this.mKeyOsPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_BRAND:
+ this.mDeviceBrand = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_DEVICE:
+ this.mDeviceName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_PRODUCT:
+ this.mDeviceProductName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_VENDOR_PATCHLEVEL:
+ this.mKeyVendorPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_BOOT_PATCHLEVEL:
+ this.mKeyBootPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void parseAttestationApplicationId(byte [] attestationApplicationId)
+ throws Exception {
+ ASN1Sequence outerSequence = ASN1Sequence.getInstance(
+ new ASN1InputStream(attestationApplicationId).readObject());
+ Map<String, Long> packageNameVersion = new HashMap<>();
+ ASN1Set packageInfoSet = (ASN1Set) outerSequence.getObjectAt(PACKAGE_INFO_SET_INDEX);
+ for (ASN1Encodable packageInfoEntry : packageInfoSet.toArray()) {
+ ASN1Sequence packageInfoSequence = (ASN1Sequence) packageInfoEntry;
+ packageNameVersion.put(
+ getUtf8FromOctetsFromAsn1(
+ packageInfoSequence.getObjectAt(PACKAGE_INFO_NAME_INDEX)),
+ getLongFromAsn1(packageInfoSequence.getObjectAt(PACKAGE_INFO_VERSION_INDEX)));
+ }
+ List<ByteString> certificateDigests = new ArrayList<>();
+ ASN1Set certificateDigestSet =
+ (ASN1Set) outerSequence.getObjectAt(PACKAGE_SIGNATURE_SET_INDEX);
+ for (ASN1Encodable certificateDigestEntry : certificateDigestSet.toArray()) {
+ certificateDigests.add(getOctetsFromAsn1(certificateDigestEntry));
+ }
+ this.mApplicationPackageNameVersion = Collections.unmodifiableMap(packageNameVersion);
+ this.mApplicationCertificateDigests = Collections.unmodifiableList(certificateDigests);
+
+ }
+
+ private VerifiedBootState getVerifiedBootStateEnum(ASN1Encodable asn1) {
+ int verifiedBoot = getEnumFromAsn1(asn1);
+ switch (verifiedBoot) {
+ case KM_VERIFIED_BOOT_STATE_VERIFIED:
+ return VerifiedBootState.VERIFIED;
+ case KM_VERIFIED_BOOT_STATE_SELF_SIGNED:
+ return VerifiedBootState.SELF_SIGNED;
+ case KM_VERIFIED_BOOT_STATE_UNVERIFIED:
+ return VerifiedBootState.UNVERIFIED;
+ case KM_VERIFIED_BOOT_STATE_FAILED:
+ return VerifiedBootState.FAILED;
+ default:
+ throw new IllegalArgumentException("Invalid verified boot state.");
+ }
+ }
+
+ private SecurityLevel getSecurityLevelEnum(ASN1Encodable asn1) {
+ int securityLevel = getEnumFromAsn1(asn1);
+ switch (securityLevel) {
+ case KM_SECURITY_LEVEL_SOFTWARE:
+ return SecurityLevel.SOFTWARE;
+ case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+ return SecurityLevel.TRUSTED_ENVIRONMENT;
+ case KM_SECURITY_LEVEL_STRONG_BOX:
+ return SecurityLevel.STRONG_BOX;
+ default:
+ throw new IllegalArgumentException("Invalid security level.");
+ }
+ }
+
+ @NonNull
+ private ByteString getOctetsFromAsn1(ASN1Encodable asn1) {
+ return ByteString.copyFrom(((ASN1OctetString) asn1).getOctets());
+ }
+
+ @NonNull
+ private String getUtf8FromOctetsFromAsn1(ASN1Encodable asn1) {
+ return new String(((ASN1OctetString) asn1).getOctets(), StandardCharsets.UTF_8);
+ }
+
+ @NonNull
+ private int getIntegerFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Integer) asn1).getValue().intValueExact();
+ }
+
+ @NonNull
+ private long getLongFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Integer) asn1).getValue().longValueExact();
+ }
+
+ @NonNull
+ private int getEnumFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Enumerated) asn1).getValue().intValueExact();
+ }
+
+ @Nullable
+ private Boolean getBoolFromAsn1(ASN1Encodable asn1) {
+ if (asn1 instanceof ASN1Boolean) {
+ return ((ASN1Boolean) asn1).isTrue();
+ }
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index 243efb5e58ce..863f2d1a762d 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.security;
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE;
import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
@@ -44,9 +45,11 @@ import com.android.server.SystemService;
public class AttestationVerificationManagerService extends SystemService {
private static final String TAG = "AVF";
+ private final AttestationVerificationPeerDeviceVerifier mPeerDeviceVerifier;
- public AttestationVerificationManagerService(final Context context) {
+ public AttestationVerificationManagerService(final Context context) throws Exception {
super(context);
+ mPeerDeviceVerifier = new AttestationVerificationPeerDeviceVerifier(context);
}
private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
@@ -83,7 +86,7 @@ public class AttestationVerificationManagerService extends SystemService {
result.token = null;
switch (profile.getAttestationProfileId()) {
case PROFILE_SELF_TRUSTED:
- Slog.d(TAG, "Verifying Self trusted profile.");
+ Slog.d(TAG, "Verifying Self Trusted profile.");
try {
result.resultCode =
AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
@@ -92,6 +95,11 @@ public class AttestationVerificationManagerService extends SystemService {
result.resultCode = RESULT_FAILURE;
}
break;
+ case PROFILE_PEER_DEVICE:
+ Slog.d(TAG, "Verifying Peer Device profile.");
+ result.resultCode = mPeerDeviceVerifier.verifyAttestation(
+ localBindingType, requirements, attestation);
+ break;
default:
Slog.d(TAG, "No profile found, defaulting.");
result.resultCode = RESULT_UNKNOWN;
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
new file mode 100644
index 000000000000..0f8be5a77944
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY;
+
+import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Verifies Android key attestation according to the {@code PROFILE_PEER_DEVICE} profile.
+ *
+ * Trust anchors are vendor-defined via the vendor_required_attestation_certificates.xml resource.
+ * The profile is satisfied by checking all the following:
+ * * TrustAnchor match
+ * * Certificate validity
+ * * Android OS 10 or higher
+ * * Hardware backed key store
+ * * Verified boot locked
+ * * Remote Patch level must be within 1 year of local patch if local patch is less than 1 year old.
+ *
+ */
+class AttestationVerificationPeerDeviceVerifier {
+ private static final String TAG = "AVF";
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
+ private static final int MAX_PATCH_AGE_MONTHS = 12;
+
+ private final Context mContext;
+ private final Set<TrustAnchor> mTrustAnchors;
+ private final boolean mRevocationEnabled;
+ private final LocalDate mTestSystemDate;
+ private final LocalDate mTestLocalPatchDate;
+ private CertificateFactory mCertificateFactory;
+ private CertPathValidator mCertPathValidator;
+
+ private static void debugVerboseLog(String str, Throwable t) {
+ if (DEBUG) {
+ Slog.v(TAG, str, t);
+ }
+ }
+
+ private static void debugVerboseLog(String str) {
+ if (DEBUG) {
+ Slog.v(TAG, str);
+ }
+ }
+
+ AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception {
+ mContext = Objects.requireNonNull(context);
+ mCertificateFactory = CertificateFactory.getInstance("X.509");
+ mCertPathValidator = CertPathValidator.getInstance("PKIX");
+ mTrustAnchors = getTrustAnchors();
+ mRevocationEnabled = true;
+ mTestSystemDate = null;
+ mTestLocalPatchDate = null;
+ }
+
+ // Use ONLY for hermetic unit testing.
+ @VisibleForTesting
+ AttestationVerificationPeerDeviceVerifier(@NonNull Context context,
+ Set<TrustAnchor> trustAnchors, boolean revocationEnabled,
+ LocalDate systemDate, LocalDate localPatchDate) throws Exception {
+ mContext = Objects.requireNonNull(context);
+ mCertificateFactory = CertificateFactory.getInstance("X.509");
+ mCertPathValidator = CertPathValidator.getInstance("PKIX");
+ mTrustAnchors = trustAnchors;
+ mRevocationEnabled = revocationEnabled;
+ mTestSystemDate = systemDate;
+ mTestLocalPatchDate = localPatchDate;
+ }
+
+ /**
+ * Verifies attestation for public key or challenge local binding.
+ *
+ * The attestations must be suitable for {@link java.security.cert.CertificateFactory}
+ * The certificates in the attestation provided must be DER-encoded and may be supplied in
+ * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding,
+ * it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at
+ * the end by -----END CERTIFICATE-----.
+ *
+ * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported.
+ * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
+ * @param attestation Certificates should be DER encoded with leaf certificate appended first.
+ */
+ int verifyAttestation(
+ int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) {
+ int status = RESULT_FAILURE;
+
+ if (mCertificateFactory == null) {
+ debugVerboseLog("Was unable to initialize CertificateFactory onCreate.");
+ return status;
+ }
+
+ if (mCertPathValidator == null) {
+ debugVerboseLog("Was unable to initialize CertPathValidator onCreate.");
+ return status;
+ }
+
+ List<X509Certificate> certificates;
+ try {
+ certificates = getCertificates(attestation);
+ } catch (CertificateException e) {
+ debugVerboseLog("Unable to parse attestation certificates.", e);
+ return status;
+ }
+
+ if (certificates.isEmpty()) {
+ debugVerboseLog("Attestation contains no certificates.");
+ return status;
+ }
+
+ X509Certificate leafNode = certificates.get(0);
+ if (validateRequirements(localBindingType, requirements)
+ && validateCertificateChain(certificates)
+ && checkCertificateAttributes(leafNode, localBindingType, requirements)) {
+ status = RESULT_SUCCESS;
+ } else {
+ status = RESULT_FAILURE;
+ }
+ return status;
+ }
+
+ @NonNull
+ private List<X509Certificate> getCertificates(byte[] attestation)
+ throws CertificateException {
+ List<X509Certificate> certificates = new ArrayList<>();
+ ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
+ while (bis.available() > 0) {
+ certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
+ }
+
+ return certificates;
+ }
+
+ private boolean validateRequirements(int localBindingType, Bundle requirements) {
+ if (requirements.size() != 1) {
+ debugVerboseLog("Requirements does not contain exactly 1 key.");
+ return false;
+ }
+
+ if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
+ debugVerboseLog("Binding type is not supported: " + localBindingType);
+ return false;
+ }
+
+ if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) {
+ debugVerboseLog("Requirements does not contain key: " + PARAM_PUBLIC_KEY);
+ return false;
+ }
+
+ if (localBindingType == TYPE_CHALLENGE && !requirements.containsKey(PARAM_CHALLENGE)) {
+ debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean validateCertificateChain(List<X509Certificate> certificates) {
+ if (certificates.size() < 2) {
+ debugVerboseLog("Certificate chain less than 2 in size.");
+ return false;
+ }
+
+ try {
+ CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
+ PKIXParameters validationParams = new PKIXParameters(mTrustAnchors);
+ if (mRevocationEnabled) {
+ // Checks Revocation Status List based on
+ // https://developer.android.com/training/articles/security-key-attestation#certificate_status
+ PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker();
+ validationParams.addCertPathChecker(checker);
+ }
+ // Do not use built-in revocation status checker.
+ validationParams.setRevocationEnabled(false);
+ mCertPathValidator.validate(certificatePath, validationParams);
+ } catch (Throwable t) {
+ debugVerboseLog("Invalid certificate chain.", t);
+ return false;
+ }
+ return true;
+ }
+
+ private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException {
+ Set<TrustAnchor> modifiableSet = new HashSet<>();
+ try {
+ for (String certString: getTrustAnchorResources()) {
+ modifiableSet.add(
+ new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate(
+ new ByteArrayInputStream(getCertificateBytes(certString))), null));
+ }
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ throw new CertPathValidatorException("Invalid trust anchor certificate.", e);
+ }
+ return Collections.unmodifiableSet(modifiableSet);
+ }
+
+ private byte[] getCertificateBytes(String certString) {
+ String formattedCertString = certString.replaceAll("\\s+", "\n");
+ formattedCertString = formattedCertString.replaceAll(
+ "-BEGIN\\nCERTIFICATE-", "-BEGIN CERTIFICATE-");
+ formattedCertString = formattedCertString.replaceAll(
+ "-END\\nCERTIFICATE-", "-END CERTIFICATE-");
+ return formattedCertString.getBytes(UTF_8);
+ }
+
+ private String[] getTrustAnchorResources() {
+ return mContext.getResources().getStringArray(
+ R.array.vendor_required_attestation_certificates);
+ }
+
+ private boolean checkCertificateAttributes(
+ X509Certificate leafCertificate, int localBindingType, Bundle requirements) {
+ AndroidKeystoreAttestationVerificationAttributes attestationAttributes;
+ try {
+ attestationAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ leafCertificate);
+ } catch (Throwable t) {
+ debugVerboseLog("Could not get ParsedAttestationAttributes from Certificate.", t);
+ return false;
+ }
+
+ // Checks for support of Keymaster 4.
+ if (attestationAttributes.getAttestationVersion() < 3) {
+ debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
+ return false;
+ }
+
+ // Checks for support of Keymaster 4.
+ if (attestationAttributes.getKeymasterVersion() < 4) {
+ debugVerboseLog("Keymaster version is not at least 4.");
+ return false;
+ }
+
+ // First two characters are Android OS version.
+ if (attestationAttributes.getKeyOsVersion() < 100000) {
+ debugVerboseLog("Android OS version is not 10+.");
+ return false;
+ }
+
+ if (!attestationAttributes.isAttestationHardwareBacked()) {
+ debugVerboseLog("Key is not HW backed.");
+ return false;
+ }
+
+ if (!attestationAttributes.isKeymasterHardwareBacked()) {
+ debugVerboseLog("Keymaster is not HW backed.");
+ return false;
+ }
+
+ if (attestationAttributes.getVerifiedBootState() != VERIFIED) {
+ debugVerboseLog("Boot state not Verified.");
+ return false;
+ }
+
+ try {
+ if (!attestationAttributes.isVerifiedBootLocked()) {
+ debugVerboseLog("Verified boot state is not locked.");
+ return false;
+ }
+ } catch (IllegalStateException e) {
+ debugVerboseLog("VerifiedBootLocked is not set.", e);
+ return false;
+ }
+
+ // Patch level integer YYYYMM is expected to be within 1 year of today.
+ if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) {
+ debugVerboseLog("OS patch level is not within valid range.");
+ return false;
+ }
+
+ // Patch level integer YYYYMMDD is expected to be within 1 year of today.
+ if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
+ debugVerboseLog("Boot patch level is not within valid range.");
+ return false;
+ }
+
+ if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) {
+ debugVerboseLog("Vendor patch level is not within valid range.");
+ return false;
+ }
+
+ if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
+ debugVerboseLog("Boot patch level is not within valid range.");
+ return false;
+ }
+
+ // Verify leaf public key matches provided public key.
+ if (localBindingType == TYPE_PUBLIC_KEY
+ && !Arrays.equals(requirements.getByteArray(PARAM_PUBLIC_KEY),
+ leafCertificate.getPublicKey().getEncoded())) {
+ debugVerboseLog("Provided public key does not match leaf certificate public key.");
+ return false;
+ }
+
+ // Verify challenge matches provided challenge.
+ if (localBindingType == TYPE_CHALLENGE
+ && !Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE),
+ attestationAttributes.getAttestationChallenge().toByteArray())) {
+ debugVerboseLog("Provided challenge does not match leaf certificate challenge.");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates patchLevel passed is within range of the local device patch date if local patch is
+ * not over one year old. Since the time can be changed on device, just checking the patch date
+ * is not enough. Therefore, we also confirm the patch level for the remote and local device are
+ * similar.
+ */
+ private boolean isValidPatchLevel(int patchLevel) {
+ LocalDate currentDate = mTestSystemDate != null
+ ? mTestSystemDate : LocalDate.now(ZoneId.systemDefault());
+
+ // Convert local patch date to LocalDate.
+ LocalDate localPatchDate;
+ try {
+ if (mTestLocalPatchDate != null) {
+ localPatchDate = mTestLocalPatchDate;
+ } else {
+ localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH);
+ }
+ } catch (Throwable t) {
+ debugVerboseLog("Build.VERSION.SECURITY_PATCH: "
+ + Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD");
+ return false;
+ }
+
+ // Check local patch date is not in last year of system clock.
+ if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) {
+ return true;
+ }
+
+ // Convert remote patch dates to LocalDate.
+ String remoteDeviceDateStr = String.valueOf(patchLevel);
+ if (remoteDeviceDateStr.length() != 6 && remoteDeviceDateStr.length() != 8) {
+ debugVerboseLog("Patch level is not in format YYYYMM or YYYYMMDD");
+ return false;
+ }
+
+ int patchYear = Integer.parseInt(remoteDeviceDateStr.substring(0, 4));
+ int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6));
+ LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1);
+
+ // Check patch dates are within 1 year of each other
+ boolean IsRemotePatchWithinOneYearOfLocalPatch;
+ if (remotePatchDate.compareTo(localPatchDate) > 0) {
+ IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
+ localPatchDate, remotePatchDate) <= MAX_PATCH_AGE_MONTHS;
+ } else if (remotePatchDate.compareTo(localPatchDate) < 0) {
+ IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
+ remotePatchDate, localPatchDate) <= MAX_PATCH_AGE_MONTHS;
+ } else {
+ IsRemotePatchWithinOneYearOfLocalPatch = true;
+ }
+
+ return IsRemotePatchWithinOneYearOfLocalPatch;
+ }
+
+ /**
+ * Checks certificate revocation status.
+ *
+ * Queries status list from android.googleapis.com/attestation/status and checks for
+ * the existence of certificate's serial number. If serial number exists in map, then fail.
+ */
+ private final class AndroidRevocationStatusListChecker extends PKIXCertPathChecker {
+ private static final String TOP_LEVEL_JSON_PROPERTY_KEY = "entries";
+ private static final String STATUS_PROPERTY_KEY = "status";
+ private static final String REASON_PROPERTY_KEY = "reason";
+ private String mStatusUrl;
+ private JSONObject mJsonStatusMap;
+
+ @Override
+ public void init(boolean forward) throws CertPathValidatorException {
+ mStatusUrl = getRevocationListUrl();
+ if (mStatusUrl == null || mStatusUrl.isEmpty()) {
+ throw new CertPathValidatorException(
+ "R.string.vendor_required_attestation_revocation_list_url is empty.");
+ }
+ // TODO(b/221067843): Update to only pull status map on non critical path and if
+ // out of date (24hrs).
+ mJsonStatusMap = getStatusMap(mStatusUrl);
+ }
+
+ @Override
+ public boolean isForwardCheckingSupported() {
+ return false;
+ }
+
+ @Override
+ public Set<String> getSupportedExtensions() {
+ return null;
+ }
+
+ @Override
+ public void check(Certificate cert, Collection<String> unresolvedCritExts)
+ throws CertPathValidatorException {
+ X509Certificate x509Certificate = (X509Certificate) cert;
+ // The json key is the certificate's serial number converted to lowercase hex.
+ String serialNumber = x509Certificate.getSerialNumber().toString(16);
+
+ if (serialNumber == null) {
+ throw new CertPathValidatorException("Certificate serial number can not be null.");
+ }
+
+ if (mJsonStatusMap.has(serialNumber)) {
+ JSONObject revocationStatus;
+ String status;
+ String reason;
+ try {
+ revocationStatus = mJsonStatusMap.getJSONObject(serialNumber);
+ status = revocationStatus.getString(STATUS_PROPERTY_KEY);
+ reason = revocationStatus.getString(REASON_PROPERTY_KEY);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException("Unable get properties for certificate "
+ + "with serial number " + serialNumber);
+ }
+ throw new CertPathValidatorException(
+ "Invalid certificate with serial number " + serialNumber
+ + " has status " + status
+ + " because reason " + reason);
+ }
+ }
+
+ private JSONObject getStatusMap(String stringUrl) throws CertPathValidatorException {
+ URL url;
+ try {
+ url = new URL(stringUrl);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException(
+ "Unable to get revocation status from " + mStatusUrl, t);
+ }
+
+ try (InputStream inputStream = url.openStream()) {
+ JSONObject statusListJson = new JSONObject(
+ new String(inputStream.readAllBytes(), UTF_8));
+ return statusListJson.getJSONObject(TOP_LEVEL_JSON_PROPERTY_KEY);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException(
+ "Unable to parse revocation status from " + mStatusUrl, t);
+ }
+ }
+
+ private String getRevocationListUrl() {
+ return mContext.getResources().getString(
+ R.string.vendor_required_attestation_revocation_list_url);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 411f3dcc1eb6..11fd99cf5b68 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -157,7 +157,7 @@ public interface StatusBarManagerInternal {
* @see com.android.internal.statusbar.IStatusBar#requestWindowMagnificationConnection(boolean
* request)
*/
- void requestWindowMagnificationConnection(boolean request);
+ boolean requestWindowMagnificationConnection(boolean request);
/**
* Handles a logging command from the WM shell command.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e4a969b3ca1d..b8763feaf269 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -637,12 +637,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void requestWindowMagnificationConnection(boolean request) {
+ public boolean requestWindowMagnificationConnection(boolean request) {
if (mBar != null) {
try {
mBar.requestWindowMagnificationConnection(request);
+ return true;
} catch (RemoteException ex) { }
}
+ return false;
}
@Override
@@ -856,11 +858,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onBiometricAuthenticated() {
+ public void onBiometricAuthenticated(@Modality int modality) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.onBiometricAuthenticated();
+ mBar.onBiometricAuthenticated(modality);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index fa243c023a87..20cd8f5c12f8 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -671,6 +671,7 @@ public class TrustAgentWrapper {
mContext.unbindService(mConnection);
mBound = false;
mContext.unregisterReceiver(mBroadcastReceiver);
+ mContext.unregisterReceiver(mTrustableDowngradeReceiver);
mTrustAgentService = null;
mSetTrustAgentFeaturesToken = null;
mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 6c5d9520151b..b05b44bcb1d2 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -34,6 +34,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioManager;
@@ -42,6 +43,7 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationAttributes;
@@ -106,6 +108,19 @@ final class VibrationSettings {
*/
private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;
+ /**
+ * Set of usages allowed for vibrations from system packages when the screen goes off.
+ *
+ * <p>Some examples are touch and hardware feedback, and physical emulation. When the system is
+ * playing one of these usages during the screen off event then the vibration will not be
+ * cancelled by the service.
+ */
+ private static final Set<Integer> SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST = new HashSet<>(
+ Arrays.asList(
+ USAGE_TOUCH,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_HARDWARE_FEEDBACK));
+
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -114,6 +129,7 @@ final class VibrationSettings {
private final Object mLock = new Object();
private final Context mContext;
+ private final String mSystemUiPackage;
private final SettingsObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
@@ -151,6 +167,9 @@ final class VibrationSettings {
mUidObserver = new UidObserver();
mUserReceiver = new UserObserver();
+ mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
+ .getSystemUiServiceComponent().getPackageName();
+
VibrationEffect clickEffect = createEffectFromResource(
com.android.internal.R.array.config_virtualKeyVibePattern);
VibrationEffect doubleClickEffect = createEffectFromResource(
@@ -344,27 +363,42 @@ final class VibrationSettings {
}
/**
+ * Check if given vibration should be cancelled by the service when the screen goes off.
+ *
+ * <p>When the system is entering a non-interactive state, we want to cancel vibrations in case
+ * a misbehaving app has abandoned them. However, it may happen that the system is currently
+ * playing haptic feedback as part of the transition. So we don't cancel system vibrations of
+ * usages like touch and hardware feedback, and physical emulation.
+ *
+ * @return true if the vibration should be cancelled when the screen goes off, false otherwise.
+ */
+ public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg,
+ @VibrationAttributes.Usage int usage) {
+ if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) {
+ // Usages not allowed even for system vibrations should always be cancelled.
+ return true;
+ }
+ // Only allow vibrations from System packages to continue vibrating when the screen goes off
+ return uid != Process.SYSTEM_UID && uid != 0 && !mSystemUiPackage.equals(opPkg);
+ }
+
+ /**
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
- * for touch and ringtone usages only. All other usages are allowed by this method.
+ * for ringtone usage only. All other usages are allowed by this method.
*/
@GuardedBy("mLock")
private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
+ if (usageHint != USAGE_RINGTONE) {
+ // Only ringtone vibrations are disabled when phone is on silent mode.
+ return true;
+ }
// If audio manager was not loaded yet then assume most restrictive mode.
int ringerMode = (mAudioManager == null)
? AudioManager.RINGER_MODE_SILENT
: mAudioManager.getRingerModeInternal();
-
- switch (usageHint) {
- case USAGE_TOUCH:
- case USAGE_RINGTONE:
- // Touch feedback and ringtone disabled when phone is on silent mode.
- return ringerMode != AudioManager.RINGER_MODE_SILENT;
- default:
- // All other usages ignore ringer mode settings.
- return true;
- }
+ return ringerMode != AudioManager.RINGER_MODE_SILENT;
}
/** Updates all vibration settings and triggers registered listeners. */
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 366763110565..024e319233f8 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build;
import android.os.CombinedVibration;
@@ -24,6 +25,7 @@ import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -69,9 +71,17 @@ final class VibrationStepConductor {
private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
+
+ // Signalling fields.
+ @GuardedBy("mLock")
+ private final IntArray mSignalVibratorsComplete;
+ @GuardedBy("mLock")
+ private boolean mSignalCancel = false;
@GuardedBy("mLock")
- private Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
+ private boolean mSignalCancelImmediate = false;
+ private boolean mCancelled = false;
+ private boolean mCancelledImmediately = false; // hard stop
private int mPendingVibrateSteps;
private int mRemainingStartSequentialEffectSteps;
private int mSuccessfulVibratorOnSteps;
@@ -91,6 +101,7 @@ final class VibrationStepConductor {
mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
}
}
+ this.mSignalVibratorsComplete = new IntArray(mVibrators.size());
}
@Nullable
@@ -152,6 +163,10 @@ final class VibrationStepConductor {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ if (mCancelledImmediately) {
+ return true; // Terminate.
+ }
+
// No need to check for vibration complete callbacks - if there were any, they would
// have no steps to notify anyway.
return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
@@ -166,6 +181,9 @@ final class VibrationStepConductor {
expectIsVibrationThread(true);
}
+ if (mCancelled) {
+ return Vibration.Status.CANCELLED;
+ }
if (mPendingVibrateSteps > 0
|| mRemainingStartSequentialEffectSteps > 0) {
return Vibration.Status.RUNNING;
@@ -180,50 +198,67 @@ final class VibrationStepConductor {
/**
* Blocks until the next step is due to run. The wait here may be interrupted by calling
- * {@link #notifyWakeUp} or other "notify" methods.
+ * one of the "notify" methods.
*
- * <p>This method returns false if the next step is ready to run now. If the method returns
- * true, then some waiting was done, but may have been interrupted by a wakeUp.
+ * <p>This method returns true if the next step is ready to run now. If the method returns
+ * false, then some waiting was done, but may have been interrupted by a wakeUp, and the
+ * status and isFinished of the vibration should be re-checked before calling this method again.
*
- * @return true if the method waited at all, or false if a step is ready to run now.
+ * @return true if the next step can be run now or the vibration is finished, or false if this
+ * method waited and the conductor state may have changed asynchronously, in which case this
+ * method needs to be run again.
*/
public boolean waitUntilNextStepIsDue() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- // It's necessary to re-process callbacks if they come in after acquiring the lock to
- // start waiting, but we don't want to hold the lock while processing them.
- // The loop goes until there are no pending callbacks to process.
- while (true) {
- // TODO: cancellation checking could also be integrated here, instead of outside in
- // VibrationThread.
- processVibratorCompleteCallbacks();
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- // Steps resumed by vibrator complete callback should be played right away.
- return false;
- }
- Step nextStep = mNextSteps.peek();
- if (nextStep == null) {
- return false;
- }
- long waitMillis = nextStep.calculateWaitTime();
- if (waitMillis <= 0) {
+
+ processAllNotifySignals();
+ if (mCancelledImmediately) {
+ // Don't try to run a step for immediate cancel, although there should be none left.
+ // Non-immediate cancellation may have cleanup steps, so it continues processing.
+ return false;
+ }
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ return true; // Resumed step ready.
+ }
+ Step nextStep = mNextSteps.peek();
+ if (nextStep == null) {
+ return true; // Finished
+ }
+ long waitMillis = nextStep.calculateWaitTime();
+ if (waitMillis <= 0) {
+ return true; // Regular step ready
+ }
+ synchronized (mLock) {
+ // Double check for signals before sleeping, as their notify wouldn't interrupt a fresh
+ // wait.
+ if (hasPendingNotifySignalLocked()) {
+ // Don't run the next step, it will loop back to this method and process them.
return false;
}
- synchronized (mLock) {
- // Double check for missed wake-ups before sleeping.
- if (!mCompletionNotifiedVibrators.isEmpty()) {
- continue; // Start again: processVibratorCompleteCallbacks will consume it.
- }
- try {
- mLock.wait(waitMillis);
- } catch (InterruptedException e) {
- }
- return true;
+ try {
+ mLock.wait(waitMillis);
+ } catch (InterruptedException e) {
}
+ return false; // Caller needs to check isFinished and maybe wait again.
}
}
+ @Nullable
+ private Step pollNext() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
+ // "next run time".
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ return mPendingOnVibratorCompleteSteps.poll();
+ }
+ return mNextSteps.poll();
+ }
+
/**
* Play and remove the step at the top of this queue, and also adds the next steps generated
* to be played next.
@@ -255,20 +290,27 @@ final class VibrationStepConductor {
}
/**
- * Wake up the execution thread, which may be waiting until the next step is due.
- * The caller is responsible for diverting VibrationThread execution.
+ * Notify the execution that cancellation is requested. This will be acted upon
+ * asynchronously in the VibrationThread.
*
- * <p>At the moment this is used after the signal is set that a cancellation needs to be
- * processed. The actual cancellation will be invoked from the VibrationThread.
+ * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
*/
- public void notifyWakeUp() {
+ public void notifyCancelled(boolean immediate) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
-
synchronized (mLock) {
+ if (immediate && mSignalCancelImmediate || mSignalCancel) {
+ // Nothing to update: already cancelled previously.
+ return;
+ }
+ mSignalCancelImmediate |= immediate;
+ mSignalCancel = true;
mLock.notify();
}
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel requested, immediate=" + immediate);
+ }
}
/**
@@ -287,7 +329,7 @@ final class VibrationStepConductor {
}
synchronized (mLock) {
- mCompletionNotifiedVibrators.offer(vibratorId);
+ mSignalVibratorsComplete.add(vibratorId);
mLock.notify();
}
}
@@ -310,23 +352,80 @@ final class VibrationStepConductor {
synchronized (mLock) {
for (int i = 0; i < mVibrators.size(); i++) {
- mCompletionNotifiedVibrators.offer(mVibrators.keyAt(i));
+ mSignalVibratorsComplete.add(mVibrators.keyAt(i));
}
mLock.notify();
}
}
+ @GuardedBy("mLock")
+ private boolean hasPendingNotifySignalLocked() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals.
+ }
+ return (mSignalCancel && !mCancelled)
+ || (mSignalCancelImmediate && !mCancelledImmediately)
+ || (mSignalVibratorsComplete.size() > 0);
+ }
+
+ /**
+ * Process any notified cross-thread signals, applying the necessary VibrationThread state
+ * changes.
+ */
+ private void processAllNotifySignals() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ int[] vibratorsToProcess = null;
+ boolean doCancel = false;
+ boolean doCancelImmediate = false;
+ // Swap out the queue of completions to process.
+ synchronized (mLock) {
+ if (mSignalCancelImmediate) {
+ if (mCancelledImmediately) {
+ Slog.wtf(TAG, "Immediate cancellation signal processed twice");
+ }
+ // This should only happen once.
+ doCancelImmediate = true;
+ }
+ if (mSignalCancel && !mCancelled) {
+ doCancel = true;
+ }
+ if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
+ vibratorsToProcess = mSignalVibratorsComplete.toArray(); // makes a copy
+ mSignalVibratorsComplete.clear();
+ }
+ }
+
+ // Force cancellation means stop everything and clear all steps, so the execution loop
+ // shouldn't come back to this method. To observe explicitly: this drops vibrator
+ // completion signals that were collected in this call, but we won't process them
+ // anyway as all steps are cancelled.
+ if (doCancelImmediate) {
+ processCancelImmediately();
+ return;
+ }
+ if (doCancel) {
+ processCancel();
+ }
+ if (vibratorsToProcess != null) {
+ processVibratorsComplete(vibratorsToProcess);
+ }
+ }
+
/**
* Cancel the current queue, replacing all remaining steps with respective clean-up steps.
*
- * <p>This will remove all steps and replace them with respective
+ * <p>This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void cancel() {
+ public void processCancel() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ mCancelled = true;
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
// and clean up steps are added back to the queue, so they can handle the callback.
List<Step> cleanUpSteps = new ArrayList<>();
@@ -344,11 +443,13 @@ final class VibrationStepConductor {
*
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void cancelImmediately() {
+ public void processCancelImmediately() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ mCancelledImmediately = true;
+ mCancelled = true;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
@@ -356,44 +457,20 @@ final class VibrationStepConductor {
mPendingVibrateSteps = 0;
}
- @Nullable
- private Step pollNext() {
- if (Build.IS_DEBUGGABLE) {
- expectIsVibrationThread(true);
- }
-
- // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
- // "next run time".
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- return mPendingOnVibratorCompleteSteps.poll();
- }
- return mNextSteps.poll();
- }
-
/**
- * Process any notified vibrator completions.
+ * Processes the vibrators that have sent their complete callbacks. A step is found that will
+ * accept the completion callback, and this step is brought forward for execution in the next
+ * run.
*
* <p>This assumes only one of the next steps is waiting on this given vibrator, so the
* first step found will be resumed by this method, in no particular order.
*/
- private void processVibratorCompleteCallbacks() {
+ private void processVibratorsComplete(@NonNull int[] vibratorsToProcess) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- Queue<Integer> vibratorsToProcess;
- // Swap out the queue of completions to process.
- synchronized (mLock) {
- if (mCompletionNotifiedVibrators.isEmpty()) {
- return; // Nothing to do.
- }
-
- vibratorsToProcess = mCompletionNotifiedVibrators;
- mCompletionNotifiedVibrators = new LinkedList<>();
- }
-
- while (!vibratorsToProcess.isEmpty()) {
- int vibratorId = vibratorsToProcess.poll();
+ for (int vibratorId : vibratorsToProcess) {
Iterator<Step> it = mNextSteps.iterator();
while (it.hasNext()) {
Step step = it.next();
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 3fef7f2a747e..26f7e7dee757 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -108,7 +108,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- cancel();
+ mStepConductor.notifyCancelled(/* immediate= */ false);
}
@Override
@@ -168,28 +168,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
/** Cancel current vibration and ramp down the vibrators gracefully. */
public void cancel() {
- if (mStop) {
- // Already cancelled, running clean-up steps.
- return;
- }
- mStop = true;
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled");
- }
- mStepConductor.notifyWakeUp();
+ mStepConductor.notifyCancelled(/* immediate= */ false);
}
/** Cancel current vibration and shuts off the vibrators immediately. */
public void cancelImmediately() {
- if (mForceStop) {
- // Already forced the thread to stop, wait for it to finish.
- return;
- }
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled immediately");
- }
- mStop = mForceStop = true;
- mStepConductor.notifyWakeUp();
+ mStepConductor.notifyCancelled(/* immediate= */ true);
}
/** Notify current vibration that a synced step has completed. */
@@ -217,13 +201,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
try {
mStepConductor.prepareToStart();
-
while (!mStepConductor.isFinished()) {
- // Skip wait and next step if mForceStop already happened.
- boolean waited = mForceStop || mStepConductor.waitUntilNextStepIsDue();
- // If we waited, don't run the next step, but instead re-evaluate cancellation
- // status
- if (!waited) {
+ boolean readyToRun = mStepConductor.waitUntilNextStepIsDue();
+ // If we waited, don't run the next step, but instead re-evaluate status.
+ if (readyToRun) {
if (DEBUG) {
Slog.d(TAG, "Play vibration consuming next step...");
}
@@ -232,23 +213,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
mStepConductor.runNextStep();
}
- if (mForceStop) {
- // Cancel every step and stop playing them right away, even clean-up steps.
- mStepConductor.cancelImmediately();
- clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
- break;
- }
-
- Vibration.Status status = mStop ? Vibration.Status.CANCELLED
- : mStepConductor.calculateVibrationStatus();
+ Vibration.Status status = mStepConductor.calculateVibrationStatus();
// This block can only run once due to mCalledVibrationCompleteCallback.
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
// First time vibration stopped running, start clean-up tasks and notify
// callback immediately.
clientVibrationCompleteIfNotAlready(status);
- if (status == Vibration.Status.CANCELLED) {
- mStepConductor.cancel();
- }
}
}
} finally {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 01f9d0b94879..94d0a7b22cdc 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.os.BatteryStats;
import android.os.Binder;
@@ -62,7 +61,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
import libcore.util.NativeAllocationRegistry;
@@ -120,7 +118,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final Object mLock = new Object();
private final Context mContext;
- private final String mSystemUiPackage;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
private final Handler mHandler;
@@ -153,17 +150,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
synchronized (mLock) {
- // When the system is entering a non-interactive state, we want
- // to cancel vibrations in case a misbehaving app has abandoned
- // them. However it may happen that the system is currently playing
- // haptic feedback as part of the transition. So we don't cancel
- // system vibrations.
- if (mNextVibration != null
- && !isSystemHapticFeedback(mNextVibration.getVibration())) {
+ // When the system is entering a non-interactive state, we want to cancel
+ // vibrations in case a misbehaving app has abandoned them.
+ if (shouldCancelOnScreenOffLocked(mNextVibration)) {
clearNextVibrationLocked(Vibration.Status.CANCELLED);
}
- if (mCurrentVibration != null
- && !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
+ if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
mCurrentVibration.cancel();
}
}
@@ -203,9 +195,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
com.android.internal.R.integer.config_previousVibrationsDumpLimit);
mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
- mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
- .getSystemUiServiceComponent().getPackageName();
-
mBatteryStatsService = injector.getBatteryStatsService();
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1074,11 +1063,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
== PackageManager.PERMISSION_GRANTED;
}
- private boolean isSystemHapticFeedback(Vibration vib) {
- if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
+ @GuardedBy("mLock")
+ private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) {
+ if (vibrationThread == null) {
return false;
}
- return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
+ Vibration vib = vibrationThread.getVibration();
+ return mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ vib.uid, vib.opPkg, vib.attrs.getUsage());
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 57f77d5b7b94..d3f3abe1085d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -79,6 +79,7 @@ import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -138,7 +139,7 @@ final class AccessibilityController {
new SparseArray<>();
private SparseArray<IBinder> mFocusedWindow = new SparseArray<>();
private int mFocusedDisplay = -1;
- private boolean mIsImeVisible = false;
+ private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator;
@@ -167,8 +168,11 @@ final class AccessibilityController {
if (dc != null) {
final Display display = dc.getDisplay();
if (display != null && display.getType() != Display.TYPE_OVERLAY) {
- mDisplayMagnifiers.put(displayId, new DisplayMagnifier(
- mService, dc, display, callbacks));
+ final DisplayMagnifier magnifier = new DisplayMagnifier(
+ mService, dc, display, callbacks);
+ magnifier.notifyImeWindowVisibilityChanged(
+ mIsImeVisibleArray.get(displayId, false));
+ mDisplayMagnifiers.put(displayId, magnifier);
result = true;
}
}
@@ -263,8 +267,6 @@ final class AccessibilityController {
FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; spec={" + spec + "}");
}
- mAccessibilityWindowsPopulator.setMagnificationSpec(displayId, spec);
-
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
displayMagnifier.setMagnificationSpec(spec);
@@ -456,6 +458,19 @@ final class AccessibilityController {
return null;
}
+ boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId);
+ }
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier == null) {
+ return false;
+ }
+
+ return displayMagnifier.getMagnificationSpec(outSpec);
+ }
+
boolean hasCallbacks() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
| FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -483,11 +498,13 @@ final class AccessibilityController {
mAccessibilityTracing.logTrace(TAG + ".updateImeVisibilityIfNeeded",
FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + ";shown=" + shown);
}
- if (mIsImeVisible == shown) {
+
+ final boolean isDisplayImeVisible = mIsImeVisibleArray.get(displayId, false);
+ if (isDisplayImeVisible == shown) {
return;
}
- mIsImeVisible = shown;
+ mIsImeVisibleArray.put(displayId, shown);
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
displayMagnifier.notifyImeWindowVisibilityChanged(shown);
@@ -523,6 +540,7 @@ final class AccessibilityController {
}
public void onDisplayRemoved(int displayId) {
+ mIsImeVisibleArray.delete(displayId);
mFocusedWindow.remove(displayId);
}
@@ -757,6 +775,25 @@ final class AccessibilityController {
return spec;
}
+ boolean getMagnificationSpec(MagnificationSpec outSpec) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK);
+ }
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
+ if (spec == null) {
+ return false;
+ }
+
+ outSpec.setTo(spec);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}");
+ }
+
+ return true;
+ }
+
void getMagnificationRegion(Region outMagnificationRegion) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 43317adca04a..c0fb83ba294c 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -41,9 +41,7 @@ import android.window.WindowInfosListener;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* This class is the accessibility windows population adapter.
@@ -70,24 +68,13 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>();
@GuardedBy("mLock")
private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>();
- private final SparseArray<MagnificationSpec> mCurrentMagnificationSpec = new SparseArray<>();
- @GuardedBy("mLock")
- private final SparseArray<MagnificationSpec> mPreviousMagnificationSpec = new SparseArray<>();
@GuardedBy("mLock")
private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>();
@GuardedBy("mLock")
private boolean mWindowsNotificationEnabled = false;
- @GuardedBy("mLock")
- private final Map<IBinder, Matrix> mWindowsTransformMatrixMap = new HashMap<>();
private final Object mLock = new Object();
private final Handler mHandler;
- private final Matrix mTempMatrix1 = new Matrix();
- private final Matrix mTempMatrix2 = new Matrix();
- private final float[] mTempFloat1 = new float[9];
- private final float[] mTempFloat2 = new float[9];
- private final float[] mTempFloat3 = new float[9];
-
AccessibilityWindowsPopulator(WindowManagerService service,
AccessibilityController accessibilityController) {
mService = service;
@@ -145,55 +132,28 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
@Override
public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
DisplayInfo[] displayInfos) {
- final List<InputWindowHandle> tempVisibleWindows = new ArrayList<>();
-
- for (InputWindowHandle window : windowHandles) {
- if (window.visible && window.getWindow() != null) {
- tempVisibleWindows.add(window);
- }
- }
- final HashMap<IBinder, Matrix> windowsTransformMatrixMap =
- getWindowsTransformMatrix(tempVisibleWindows);
-
synchronized (mLock) {
- mWindowsTransformMatrixMap.clear();
- mWindowsTransformMatrixMap.putAll(windowsTransformMatrixMap);
-
mVisibleWindows.clear();
- mVisibleWindows.addAll(tempVisibleWindows);
+ for (InputWindowHandle window : windowHandles) {
+ if (window.visible && window.getWindow() != null) {
+ mVisibleWindows.add(window);
+ }
+ }
mDisplayInfos.clear();
for (final DisplayInfo displayInfo : displayInfos) {
mDisplayInfos.put(displayInfo.mDisplayId, displayInfo);
}
- if (!mHandler.hasMessages(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
- mHandler.sendEmptyMessageDelayed(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
- WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
- }
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded();
- }
- }
-
- private HashMap<IBinder, Matrix> getWindowsTransformMatrix(List<InputWindowHandle> windows) {
- synchronized (mService.mGlobalLock) {
- final HashMap<IBinder, Matrix> windowsTransformMatrixMap = new HashMap<>();
-
- for (InputWindowHandle inputWindowHandle : windows) {
- final IWindow iWindow = inputWindowHandle.getWindow();
- final WindowState windowState = iWindow != null ? mService.mWindowMap.get(
- iWindow.asBinder()) : null;
-
- if (windowState != null && windowState.shouldMagnify()) {
- final Matrix transformMatrix = new Matrix();
- windowState.getTransformationMatrix(sTempFloats, transformMatrix);
- windowsTransformMatrixMap.put(iWindow.asBinder(), transformMatrix);
+ if (mWindowsNotificationEnabled) {
+ if (!mHandler.hasMessages(
+ MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
+ mHandler.sendEmptyMessageDelayed(
+ MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
+ WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
}
+ populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
}
-
- return windowsTransformMatrixMap;
}
}
@@ -211,43 +171,14 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
}
mWindowsNotificationEnabled = register;
if (mWindowsNotificationEnabled) {
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded();
+ populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
} else {
releaseResources();
}
}
}
- /**
- * Sets the magnification spec for calculating the window bounds of all windows
- * reported from the surface flinger in the magnifying.
- *
- * @param displayId The display Id.
- * @param spec THe magnification spec.
- */
- public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
- synchronized (mLock) {
- MagnificationSpec currentMagnificationSpec = mCurrentMagnificationSpec.get(displayId);
- if (currentMagnificationSpec == null) {
- currentMagnificationSpec = new MagnificationSpec();
- currentMagnificationSpec.setTo(spec);
- mCurrentMagnificationSpec.put(displayId, currentMagnificationSpec);
-
- return;
- }
-
- MagnificationSpec previousMagnificationSpec = mPreviousMagnificationSpec.get(displayId);
- if (previousMagnificationSpec == null) {
- previousMagnificationSpec = new MagnificationSpec();
- mPreviousMagnificationSpec.put(displayId, previousMagnificationSpec);
- }
- previousMagnificationSpec.setTo(currentMagnificationSpec);
- currentMagnificationSpec.setTo(spec);
- }
- }
-
- @GuardedBy("mLock")
- private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded() {
+ private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() {
final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>();
for (final InputWindowHandle windowHandle : mVisibleWindows) {
@@ -257,15 +188,15 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
if (inputWindowHandles == null) {
inputWindowHandles = new ArrayList<>();
tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles);
+ generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId);
}
inputWindowHandles.add(windowHandle);
}
- findMagnificationSpecInverseMatrixIfNeeded(tempWindowHandleList);
final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
- getDisplaysForWindowsChanged(displayIdsForWindowsChanged, tempWindowHandleList,
- mInputWindowHandlesOnDisplays);
+ getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList,
+ mInputWindowHandlesOnDisplays);
// Clones all windows from the callback of the surface flinger.
mInputWindowHandlesOnDisplays.clear();
for (int i = 0; i < tempWindowHandleList.size(); i++) {
@@ -273,7 +204,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId));
}
- if (!displayIdsForWindowsChanged.isEmpty()) {
+ if (displayIdsForWindowsChanged.size() > 0) {
if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) {
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
displayIdsForWindowsChanged).sendToTarget();
@@ -286,8 +217,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS);
}
- @GuardedBy("mLock")
- private static void getDisplaysForWindowsChanged(List<Integer> outDisplayIdsForWindowsChanged,
+ private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged,
SparseArray<List<InputWindowHandle>> newWindowsList,
SparseArray<List<InputWindowHandle>> oldWindowsList) {
for (int i = 0; i < newWindowsList.size(); i++) {
@@ -295,14 +225,13 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
final List<InputWindowHandle> newWindows = newWindowsList.get(displayId);
final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId);
- if (hasWindowsChanged(newWindows, oldWindows)) {
+ if (hasWindowsChangedLocked(newWindows, oldWindows)) {
outDisplayIdsForWindowsChanged.add(displayId);
}
}
}
- @GuardedBy("mLock")
- private static boolean hasWindowsChanged(List<InputWindowHandle> newWindows,
+ private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows,
List<InputWindowHandle> oldWindows) {
if (oldWindows == null || oldWindows.size() != newWindows.size()) {
return true;
@@ -324,195 +253,34 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
return false;
}
- @GuardedBy("mLock")
- private void findMagnificationSpecInverseMatrixIfNeeded(SparseArray<List<InputWindowHandle>>
- windowHandleList) {
- MagnificationSpec currentMagnificationSpec;
- MagnificationSpec previousMagnificationSpec;
- for (int i = 0; i < windowHandleList.size(); i++) {
- final int displayId = windowHandleList.keyAt(i);
- List<InputWindowHandle> inputWindowHandles = windowHandleList.get(displayId);
-
- final MagnificationSpec currentSpec = mCurrentMagnificationSpec.get(displayId);
- if (currentSpec == null) {
- continue;
- }
- currentMagnificationSpec = new MagnificationSpec();
- currentMagnificationSpec.setTo(currentSpec);
-
- final MagnificationSpec previousSpec = mPreviousMagnificationSpec.get(displayId);
-
- if (previousSpec == null) {
- final Matrix inverseMatrixForCurrentSpec = new Matrix();
- generateInverseMatrix(currentMagnificationSpec, inverseMatrixForCurrentSpec);
- mMagnificationSpecInverseMatrix.put(displayId, inverseMatrixForCurrentSpec);
- continue;
- }
- previousMagnificationSpec = new MagnificationSpec();
- previousMagnificationSpec.setTo(previousSpec);
-
- generateInverseMatrixBasedOnProperMagnificationSpecForDisplay(inputWindowHandles,
- currentMagnificationSpec, previousMagnificationSpec);
- }
- }
-
- @GuardedBy("mLock")
- private void generateInverseMatrixBasedOnProperMagnificationSpecForDisplay(
- List<InputWindowHandle> inputWindowHandles, MagnificationSpec currentMagnificationSpec,
- MagnificationSpec previousMagnificationSpec) {
- // To decrease the counts of holding the WindowManagerService#mGlogalLock in
- // the method, getWindowTransformMatrix(), this for loop begins from the bottom
- // to top of the z-order windows.
- for (int index = inputWindowHandles.size() - 1; index >= 0; index--) {
- final Matrix windowTransformMatrix = mTempMatrix2;
- final InputWindowHandle windowHandle = inputWindowHandles.get(index);
- final IBinder iBinder = windowHandle.getWindow().asBinder();
-
- if (getWindowTransformMatrix(iBinder, windowTransformMatrix)) {
- generateMagnificationSpecInverseMatrix(windowHandle, currentMagnificationSpec,
- previousMagnificationSpec, windowTransformMatrix);
-
- break;
- }
- }
- }
-
- @GuardedBy("mLock")
- private boolean getWindowTransformMatrix(IBinder iBinder, Matrix outTransform) {
- final Matrix windowMatrix = iBinder != null
- ? mWindowsTransformMatrixMap.get(iBinder) : null;
-
- if (windowMatrix == null) {
- return false;
- }
- outTransform.set(windowMatrix);
-
- return true;
- }
-
- /**
- * Generates the inverse matrix based on the proper magnification spec.
- * The magnification spec associated with the InputWindowHandle might not the current
- * spec set by WM, which might be the previous one. To find the appropriate spec,
- * we store two consecutive magnification specs, and found out which one is the proper
- * one closing the identity matrix for generating the inverse matrix.
- *
- * @param inputWindowHandle The window from the surface flinger.
- * @param currentMagnificationSpec The current magnification spec.
- * @param previousMagnificationSpec The previous magnification spec.
- * @param transformMatrix The transform matrix of the window doesn't consider the
- * magnifying effect.
- */
- @GuardedBy("mLock")
- private void generateMagnificationSpecInverseMatrix(InputWindowHandle inputWindowHandle,
- @NonNull MagnificationSpec currentMagnificationSpec,
- @NonNull MagnificationSpec previousMagnificationSpec, Matrix transformMatrix) {
-
- final float[] identityMatrixFloatsForCurrentSpec = mTempFloat1;
- computeIdentityMatrix(inputWindowHandle, currentMagnificationSpec,
- transformMatrix, identityMatrixFloatsForCurrentSpec);
- final float[] identityMatrixFloatsForPreviousSpec = mTempFloat2;
- computeIdentityMatrix(inputWindowHandle, previousMagnificationSpec,
- transformMatrix, identityMatrixFloatsForPreviousSpec);
-
- Matrix inverseMatrixForMagnificationSpec = new Matrix();
- if (selectProperMagnificationSpecByComparingIdentityDegree(
- identityMatrixFloatsForCurrentSpec, identityMatrixFloatsForPreviousSpec)) {
- generateInverseMatrix(currentMagnificationSpec,
- inverseMatrixForMagnificationSpec);
-
- // Choosing the current spec means the previous spec is out of date,
- // so removing it. And if the current spec is no magnifying, meaning
- // the magnifying is done so removing the inverse matrix of this display.
- mPreviousMagnificationSpec.remove(inputWindowHandle.displayId);
- if (currentMagnificationSpec.isNop()) {
- mCurrentMagnificationSpec.remove(inputWindowHandle.displayId);
- mMagnificationSpecInverseMatrix.remove(inputWindowHandle.displayId);
- return;
- }
- } else {
- generateInverseMatrix(previousMagnificationSpec,
- inverseMatrixForMagnificationSpec);
+ private void generateMagnificationSpecInverseMatrixLocked(int displayId) {
+ MagnificationSpec spec = new MagnificationSpec();
+ if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) {
+ mMagnificationSpecInverseMatrix.remove(displayId);
+ return;
}
-
- mMagnificationSpecInverseMatrix.put(inputWindowHandle.displayId,
- inverseMatrixForMagnificationSpec);
- }
-
- /**
- * Computes the identity matrix for generating the
- * inverse matrix based on below formula under window is at the stable state:
- * inputWindowHandle#transform * MagnificationSpecMatrix * WindowState#transform
- * = IdentityMatrix
- */
- @GuardedBy("mLock")
- private void computeIdentityMatrix(InputWindowHandle inputWindowHandle,
- @NonNull MagnificationSpec magnificationSpec,
- Matrix transformMatrix, float[] magnifyMatrixFloats) {
- final Matrix specMatrix = mTempMatrix1;
- transformMagnificationSpecToMatrix(magnificationSpec, specMatrix);
-
- final Matrix resultMatrix = new Matrix(inputWindowHandle.transform);
- resultMatrix.preConcat(specMatrix);
- resultMatrix.preConcat(transformMatrix);
-
- resultMatrix.getValues(magnifyMatrixFloats);
- }
-
- /**
- * @return true if selecting the magnification spec one, otherwise selecting the
- * magnification spec two.
- */
- @GuardedBy("mLock")
- private boolean selectProperMagnificationSpecByComparingIdentityDegree(
- float[] magnifyMatrixFloatsForSpecOne,
- float[] magnifyMatrixFloatsForSpecTwo) {
- final float[] IdentityMatrixValues = mTempFloat3;
- Matrix.IDENTITY_MATRIX.getValues(IdentityMatrixValues);
-
- final float scaleDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MSCALE_X]
- - magnifyMatrixFloatsForSpecOne[Matrix.MSCALE_X]);
- final float scaleDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MSCALE_X]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MSCALE_X]);
- final float offsetXDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MTRANS_X]
- - magnifyMatrixFloatsForSpecOne[Matrix.MTRANS_X]);
- final float offsetXDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MTRANS_X]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MTRANS_X]);
- final float offsetYDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MTRANS_Y]
- - magnifyMatrixFloatsForSpecOne[Matrix.MTRANS_Y]);
- final float offsetYDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MTRANS_Y]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MTRANS_Y]);
- final float offsetDiffForSpecOne = offsetXDiffForSpecOne
- + offsetYDiffForSpecOne;
- final float offsetDiffForSpecTwo = offsetXDiffForSpecTwo
- + offsetYDiffForSpecTwo;
-
- return Float.compare(scaleDiffForSpecTwo, scaleDiffForSpecOne) > 0
- || (Float.compare(scaleDiffForSpecTwo, scaleDiffForSpecOne) == 0
- && Float.compare(offsetDiffForSpecTwo, offsetDiffForSpecOne) > 0);
- }
-
- @GuardedBy("mLock")
- private static void generateInverseMatrix(MagnificationSpec spec, Matrix outMatrix) {
- outMatrix.reset();
+ sTempFloats[Matrix.MSCALE_X] = spec.scale;
+ sTempFloats[Matrix.MSKEW_Y] = 0;
+ sTempFloats[Matrix.MSKEW_X] = 0;
+ sTempFloats[Matrix.MSCALE_Y] = spec.scale;
+ sTempFloats[Matrix.MTRANS_X] = spec.offsetX;
+ sTempFloats[Matrix.MTRANS_Y] = spec.offsetY;
+ sTempFloats[Matrix.MPERSP_0] = 0;
+ sTempFloats[Matrix.MPERSP_1] = 0;
+ sTempFloats[Matrix.MPERSP_2] = 1;
final Matrix tempMatrix = new Matrix();
- transformMagnificationSpecToMatrix(spec, tempMatrix);
+ tempMatrix.setValues(sTempFloats);
+
+ final Matrix inverseMatrix = new Matrix();
+ final boolean result = tempMatrix.invert(inverseMatrix);
- final boolean result = tempMatrix.invert(outMatrix);
if (!result) {
Slog.e(TAG, "Can't inverse the magnification spec matrix with the "
- + "magnification spec = " + spec);
- outMatrix.reset();
+ + "magnification spec = " + spec + " on the displayId = " + displayId);
+ return;
}
- }
-
- @GuardedBy("mLock")
- private static void transformMagnificationSpecToMatrix(MagnificationSpec spec,
- Matrix outMatrix) {
- outMatrix.reset();
- outMatrix.postScale(spec.scale, spec.scale);
- outMatrix.postTranslate(spec.offsetX, spec.offsetY);
+ mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix);
}
private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) {
@@ -542,9 +310,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
mMagnificationSpecInverseMatrix.clear();
mVisibleWindows.clear();
mDisplayInfos.clear();
- mCurrentMagnificationSpec.clear();
- mPreviousMagnificationSpec.clear();
- mWindowsTransformMatrixMap.clear();
mWindowsNotificationEnabled = false;
mHandler.removeCallbacksAndMessages(null);
}
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 4df2e1782e4f..06c58baee1f9 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -43,9 +43,13 @@ public abstract class ActivityInterceptorCallback {
public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/**
- * Called when an activity is successfully launched.
+ * Called when an activity is successfully launched. The intent included in the
+ * ActivityInterceptorInfo may have changed from the one sent in
+ * {@link #intercept(ActivityInterceptorInfo)}, due to the return from
+ * {@link #intercept(ActivityInterceptorInfo)}.
*/
- public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
+ public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
+ ActivityInterceptorInfo info) {
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 287248f311a8..4e751f5a3ab3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -44,8 +44,8 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -3247,7 +3247,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// the best capture timing (e.g. IME window capture),
// No need additional task capture while task is controlled by RecentsAnimation.
if (mAtmService.mWindowManager.mTaskSnapshotController != null
- && !task.isAnimatingByRecents()) {
+ && !(task.isAnimatingByRecents()
+ || mTransitionController.inRecentsTransition(task))) {
final ArraySet<Task> tasks = Sets.newArraySet(task);
mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
mAtmService.mWindowManager.mTaskSnapshotController
@@ -5084,10 +5085,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (!visible) {
- final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget(
- DisplayContent.IME_TARGET_INPUT);
- mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null
- && imeInputTarget.getWindow().mActivityRecord == this
+ final InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
+ mLastImeShown = imeInputTarget != null && imeInputTarget.getWindowState() != null
+ && imeInputTarget.getWindowState().mActivityRecord == this
&& mDisplayContent.mInputMethodWindow != null
&& mDisplayContent.mInputMethodWindow.isVisible();
mImeInsetsFrozenUntilStartInput = true;
@@ -7514,8 +7514,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int parentWindowingMode =
newParentConfiguration.windowConfiguration.getWindowingMode();
final boolean isFixedOrientationLetterboxAllowed =
- isSplitScreenWindowingMode(parentWindowingMode)
- || parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+ parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
|| parentWindowingMode == WINDOWING_MODE_FULLSCREEN;
// TODO(b/181207944): Consider removing the if condition and always run
// resolveFixedOrientationConfiguration() since this should be applied for all cases.
@@ -8212,6 +8211,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ // We want to collect the ActivityRecord if the windowing mode is changed, so that it will
+ // dispatch app transition finished event correctly at the end.
+ // Check #isVisible() because we don't want to animate for activity that stays invisible.
+ // Activity with #isVisibleRequested() changed should be collected when that is requested.
+ if (mTransitionController.isShellTransitionsEnabled() && isVisible()
+ && isVisibleRequested()) {
+ final int projectedWindowingMode =
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED
+ ? newParentConfig.windowConfiguration.getWindowingMode()
+ : getRequestedOverrideWindowingMode();
+ if (getWindowingMode() != projectedWindowingMode) {
+ mTransitionController.collect(this);
+ }
+ }
if (mCompatDisplayInsets != null) {
Configuration overrideConfig = getRequestedOverrideConfiguration();
// Adapt to changes in orientation locking. The app is still non-resizable, but
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 316bf2017585..19d5449254ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -16,10 +16,6 @@
package com.android.server.wm;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.os.IBinder;
@@ -101,8 +97,7 @@ class ActivityRecordInputSink {
mToken = inputChannel.getToken();
mInputEventReceiver = createInputEventReceiver(inputChannel);
}
- if (mDisabled || !mIsCompatEnabled || mActivityRecord.isAnimating(TRANSITION | PARENTS,
- ANIMATION_TYPE_APP_TRANSITION)) {
+ if (mDisabled || !mIsCompatEnabled || mActivityRecord.isInTransition()) {
// TODO(b/208662670): Investigate if we can have feature active during animations.
mInputWindowHandleWrapper.setToken(null);
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 658a17bd9ec7..c5fcd12efa78 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -188,10 +188,7 @@ class ActivityStartInterceptor {
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
- new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
- mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
- mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
- mActivityOptions);
+ getInterceptorInfo();
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
@@ -412,9 +409,17 @@ class ActivityStartInterceptor {
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
+ ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo();
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
- callback.onActivityLaunched(taskInfo, activityInfo);
+ callback.onActivityLaunched(taskInfo, activityInfo, info);
}
}
+
+ private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo() {
+ return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
+ mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
+ mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
+ mActivityOptions);
+ }
}
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 3d54b27c819b..6befefda8a12 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.server.wm.ActivityRecord.INVALID_PID;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.os.Build;
import android.os.IBinder;
import android.os.Process;
@@ -35,6 +36,7 @@ import com.android.server.criticalevents.CriticalEventLog;
import java.io.File;
import java.util.ArrayList;
+import java.util.OptionalInt;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -75,7 +77,33 @@ class AnrController {
activity.inputDispatchingTimedOut(reason, INVALID_PID);
}
- void notifyWindowUnresponsive(IBinder inputToken, String reason) {
+
+ /**
+ * Notify a window was unresponsive.
+ *
+ * @param token - the input token of the window
+ * @param pid - the pid of the window, if known
+ * @param reason - the reason for the window being unresponsive
+ */
+ void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
+ @NonNull String reason) {
+ if (notifyWindowUnresponsive(token, reason)) {
+ return;
+ }
+ if (!pid.isPresent()) {
+ Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was unresponsive.");
+ return;
+ }
+ notifyWindowUnresponsive(pid.getAsInt(), reason);
+ }
+
+ /**
+ * Notify a window identified by its input token was unresponsive.
+ *
+ * @return true if the window was identified by the given input token and the request was
+ * handled, false otherwise.
+ */
+ private boolean notifyWindowUnresponsive(@NonNull IBinder inputToken, String reason) {
preDumpIfLockTooSlow();
final int pid;
final boolean aboveSystem;
@@ -83,10 +111,8 @@ class AnrController {
synchronized (mService.mGlobalLock) {
InputTarget target = mService.getInputTargetFromToken(inputToken);
if (target == null) {
- Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request");
- return;
+ return false;
}
-
WindowState windowState = target.getWindowState();
pid = target.getPid();
// Blame the activity if the input token belongs to the window. If the target is
@@ -102,34 +128,63 @@ class AnrController {
} else {
mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason);
}
+ return true;
+ }
+
+ /**
+ * Notify a window owned by the provided pid was unresponsive.
+ */
+ private void notifyWindowUnresponsive(int pid, String reason) {
+ Slog.i(TAG_WM, "ANR in input window owned by pid=" + pid + ". Reason: " + reason);
+ dumpAnrStateLocked(null /* activity */, null /* windowState */, reason);
+
+ // We cannot determine the z-order of the window, so place the anr dialog as high
+ // as possible.
+ mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, reason);
+ }
+
+ /**
+ * Notify a window was responsive after previously being unresponsive.
+ *
+ * @param token - the input token of the window
+ * @param pid - the pid of the window, if known
+ */
+ void notifyWindowResponsive(@NonNull IBinder token, @NonNull OptionalInt pid) {
+ if (notifyWindowResponsive(token)) {
+ return;
+ }
+ if (!pid.isPresent()) {
+ Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was responsive.");
+ return;
+ }
+ notifyWindowResponsive(pid.getAsInt());
}
- void notifyWindowResponsive(IBinder inputToken) {
+ /**
+ * Notify a window identified by its input token was responsive after previously being
+ * unresponsive.
+ *
+ * @return true if the window was identified by the given input token and the request was
+ * handled, false otherwise.
+ */
+ private boolean notifyWindowResponsive(@NonNull IBinder inputToken) {
final int pid;
synchronized (mService.mGlobalLock) {
InputTarget target = mService.getInputTargetFromToken(inputToken);
if (target == null) {
- Slog.e(TAG_WM, "Unknown token, dropping notifyWindowConnectionResponsive request");
- return;
+ return false;
}
pid = target.getPid();
}
mService.mAmInternal.inputDispatchingResumed(pid);
+ return true;
}
- void notifyGestureMonitorUnresponsive(int gestureMonitorPid, String reason) {
- preDumpIfLockTooSlow();
- synchronized (mService.mGlobalLock) {
- Slog.i(TAG_WM, "ANR in gesture monitor owned by pid:" + gestureMonitorPid
- + ". Reason: " + reason);
- dumpAnrStateLocked(null /* activity */, null /* windowState */, reason);
- }
- mService.mAmInternal.inputDispatchingTimedOut(gestureMonitorPid, /* aboveSystem */ true,
- reason);
- }
-
- void notifyGestureMonitorResponsive(int gestureMonitorPid) {
- mService.mAmInternal.inputDispatchingResumed(gestureMonitorPid);
+ /**
+ * Notify a window owned by the provided pid was responsive after previously being unresponsive.
+ */
+ private void notifyWindowResponsive(int pid) {
+ mService.mAmInternal.inputDispatchingResumed(pid);
}
/**
@@ -228,12 +283,7 @@ class AnrController {
mService.mAtmService.saveANRState(reason);
}
- private boolean isWindowAboveSystem(WindowState windowState) {
- if (windowState == null) {
- // If the window state is not available we cannot easily determine its z order. Try to
- // place the anr dialog as high as possible.
- return true;
- }
+ private boolean isWindowAboveSystem(@NonNull WindowState windowState) {
int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
return windowState.mBaseLayer > systemAlertLayer;
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 5c13e8192701..9e889ad11b8e 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -94,6 +94,9 @@ class AsyncRotationController extends FadeAnimationController implements Consume
/** Whether the start transaction of the transition is committed (by shell). */
private boolean mIsStartTransactionCommitted;
+ /** Whether the target windows have been requested to sync their draw transactions. */
+ private boolean mIsSyncDrawRequested;
+
private SeamlessRotator mRotator;
private final int mOriginalRotation;
@@ -139,22 +142,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
// Legacy animation doesn't need to wait for the start transaction.
- mIsStartTransactionCommitted = mTransitionOp == OP_LEGACY;
- if (mIsStartTransactionCommitted) return;
- // The transition sync group may be finished earlier because it doesn't wait for these
- // target windows. But the windows still need to use sync transaction to keep the appearance
- // in previous rotation, so request a no-op sync to keep the state.
- for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- if (mHasScreenRotationAnimation
- && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
- // The windows are hidden (leash is alpha 0) before finishing drawing so it is
- // unnecessary to request sync.
- continue;
- }
- final WindowToken token = mTargetWindowTokens.keyAt(i);
- for (int j = token.getChildCount() - 1; j >= 0; j--) {
- token.getChildAt(j).applyWithNextDraw(t -> {});
- }
+ if (mTransitionOp == OP_LEGACY) {
+ mIsStartTransactionCommitted = true;
+ } else if (displayContent.mTransitionController.useShellTransitionsRotation()) {
+ keepAppearanceInPreviousRotation();
}
}
@@ -194,6 +185,30 @@ class AsyncRotationController extends FadeAnimationController implements Consume
mTargetWindowTokens.put(w.mToken, new Operation(action));
}
+ /**
+ * Enables {@link #handleFinishDrawing(WindowState, SurfaceControl.Transaction)} to capture the
+ * draw transactions of the target windows if needed.
+ */
+ void keepAppearanceInPreviousRotation() {
+ // The transition sync group may be finished earlier because it doesn't wait for these
+ // target windows. But the windows still need to use sync transaction to keep the appearance
+ // in previous rotation, so request a no-op sync to keep the state.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ if (mHasScreenRotationAnimation
+ && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
+ // The windows are hidden (leash is alpha 0) before finishing drawing so it is
+ // unnecessary to request sync.
+ continue;
+ }
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ token.getChildAt(j).applyWithNextDraw(t -> {});
+ }
+ }
+ mIsSyncDrawRequested = true;
+ if (DEBUG) Slog.d(TAG, "Requested to sync draw transaction");
+ }
+
/** Lets the window fit in new rotation naturally. */
private void finishOp(WindowToken windowToken) {
final Operation op = mTargetWindowTokens.remove(windowToken);
@@ -433,7 +448,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
*/
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
- || !w.mTransitionController.inTransition()) {
+ || !mIsSyncDrawRequested || !w.mTransitionController.inTransition()) {
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 487aff63b555..23f14a7b7a16 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -53,7 +53,7 @@ class BackNavigationController {
* Returns true if the back predictability feature is enabled
*/
static boolean isEnabled() {
- return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
}
static boolean isScreenshotEnabled() {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index afa4f190c6e3..08a9da467162 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -24,8 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
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.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.app.WindowConfigurationProto.WINDOWING_MODE;
@@ -475,33 +473,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return WindowConfiguration.inMultiWindowMode(windowingMode);
}
- /** Returns true if this container is currently in split-screen windowing mode. */
- public boolean inSplitScreenWindowingMode() {
- /*@WindowConfiguration.WindowingMode*/ int windowingMode =
- mFullConfiguration.windowConfiguration.getWindowingMode();
-
- return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- /** Returns true if this container is currently in split-screen secondary windowing mode. */
- public boolean inSplitScreenSecondaryWindowingMode() {
- /*@WindowConfiguration.WindowingMode*/ int windowingMode =
- mFullConfiguration.windowConfiguration.getWindowingMode();
-
- return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- public boolean inSplitScreenPrimaryWindowingMode() {
- return mFullConfiguration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- }
-
/**
- * Returns true if this container can be put in either
- * {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
- * {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on
- * its current state.
+ * Returns true if this container supports split-screen multi-window and can be put in
+ * split-screen based on its current state.
*/
public boolean supportsSplitScreenWindowingMode() {
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 159c71b80955..77787e1e7530 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -26,7 +26,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -592,7 +591,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* The window which receives input from the input method. This is also a candidate of the
* input method control target.
*/
- private WindowState mImeInputTarget;
+ private InputTarget mImeInputTarget;
+
+ /**
+ * The last ime input target processed from setImeLayeringTargetInner
+ * this is to ensure we update the control target in the case when the IME
+ * target changes while the IME layering target stays the same, for example
+ * the case of the IME moving to a SurfaceControlViewHost backed EmbeddedWindow
+ */
+ private InputTarget mLastImeInputTarget;
/**
* This controls the visibility and animation of the input method window.
@@ -608,14 +615,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
static final int IME_TARGET_LAYERING = 0;
/**
- * Used by {@link #getImeTarget} to return the IME target which received the input connection
- * from IME.
- *
- * @see #mImeInputTarget
- */
- static final int IME_TARGET_INPUT = 1;
-
- /**
* Used by {@link #getImeTarget} to return the IME target which controls the IME insets
* visibility and animation.
*
@@ -625,7 +624,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@IntDef(flag = false, prefix = { "IME_TARGET_" }, value = {
IME_TARGET_LAYERING,
- IME_TARGET_INPUT,
IME_TARGET_CONTROL,
})
@Retention(RetentionPolicy.SOURCE)
@@ -1736,19 +1734,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
- final boolean useAsyncRotation = !mTransitionController.isShellTransitionsEnabled();
if (mFixedRotationLaunchingApp == null && r != null) {
- mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this,
- rotation);
- if (useAsyncRotation) {
- startAsyncRotation(
- // Delay the hide animation to avoid blinking by clicking navigation bar
- // that may toggle fixed rotation in a short time.
- r == mFixedRotationTransitionListener.mAnimatingRecents);
- }
+ mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
+ // Delay the hide animation to avoid blinking by clicking navigation bar that may
+ // toggle fixed rotation in a short time.
+ final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
+ || mTransitionController.isTransientLaunch(r);
+ startAsyncRotation(shouldDebounce);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
- if (useAsyncRotation) finishAsyncRotationIfPossible();
+ // Keep async rotation controller if the next transition of display is requested.
+ if (!mTransitionController.isCollecting(this)) {
+ finishAsyncRotationIfPossible();
+ }
}
mFixedRotationLaunchingApp = r;
}
@@ -1806,7 +1804,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
// Update directly because the app which will change the orientation of display is ready.
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
- mTransitionController.setSeamlessRotation(this);
sendNewConfiguration();
return;
}
@@ -3235,7 +3232,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (isRotationChanging()) {
+ if (mFixedRotationLaunchingApp != null) {
+ // A fixed-rotation transition is done, then continue to start a seamless display
+ // transition. And be fore the start transaction is applied, the non-app windows
+ // need to keep in previous rotation to avoid showing inconsistent content.
+ t.setSeamlessRotation(this);
+ if (mAsyncRotationController != null) {
+ mAsyncRotationController.keepAppearanceInPreviousRotation();
+ }
+ } else if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
@@ -3308,7 +3313,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mImeLayeringTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
}
if (mImeInputTarget != null) {
- mImeInputTarget.dumpDebug(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
+ mImeInputTarget.dumpProto(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
}
if (mImeControlTarget != null
&& mImeControlTarget.getWindow() != null) {
@@ -3871,7 +3876,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
private boolean isImeControlledByApp() {
- return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode();
+ return mImeInputTarget != null && mImeInputTarget.shouldControlIme();
}
boolean shouldImeAttachedToApp() {
@@ -3934,19 +3939,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*
* @param type The type of the IME target.
* @see #IME_TARGET_LAYERING
- * @see #IME_TARGET_INPUT
* @see #IME_TARGET_CONTROL
*/
InsetsControlTarget getImeTarget(@InputMethodTarget int type) {
switch (type) {
case IME_TARGET_LAYERING: return mImeLayeringTarget;
- case IME_TARGET_INPUT: return mImeInputTarget;
case IME_TARGET_CONTROL: return mImeControlTarget;
default:
return null;
}
}
+ InputTarget getImeInputTarget() {
+ return mImeInputTarget;
+ }
+
// IMPORTANT: When introducing new dependencies in this method, make sure that
// changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
// being called.
@@ -3995,9 +4002,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* placed at its parent's surface.
*/
private void setImeLayeringTargetInner(@Nullable WindowState target) {
- if (target == mImeLayeringTarget) {
+ /**
+ * This function is also responsible for updating the IME control target
+ * and so in the case where the IME layering target does not change
+ * but the Input target does (for example, IME moving to a SurfaceControlViewHost
+ * we have to continue executing this function, otherwise there is no work
+ * to do.
+ */
+ if (target == mImeLayeringTarget && mLastImeInputTarget == mImeInputTarget) {
return;
}
+ mLastImeInputTarget = mImeInputTarget;
+
// If the IME target is the input target, before it changes, prepare the IME screenshot
// for the last IME target when its task is applying app transition. This is for the
// better IME transition to keep IME visibility when transitioning to the next task.
@@ -4039,9 +4055,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@VisibleForTesting
- void setImeInputTarget(WindowState target) {
+ void setImeInputTarget(InputTarget target) {
mImeInputTarget = target;
- boolean canScreenshot = mImeInputTarget == null || !mImeInputTarget.isSecureLocked();
+ boolean canScreenshot = mImeInputTarget == null || mImeInputTarget.canScreenshotIme();
if (mImeWindowsContainer.setCanScreenshot(canScreenshot)) {
mWmService.requestTraversal();
}
@@ -4157,7 +4173,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* The IME input target is the window which receives input from IME. It is also a candidate
* which controls the visibility and animation of the input method window.
*/
- void updateImeInputAndControlTarget(WindowState target) {
+ void updateImeInputAndControlTarget(InputTarget target) {
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
setImeInputTarget(target);
@@ -4167,8 +4183,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
// Unfreeze IME insets after the new target updated, in case updateAboveInsetsState may
// deliver unrelated IME insets change to the non-IME requester.
- if (target != null && target.mActivityRecord != null) {
- target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ if (target != null) {
+ target.unfreezeInsetsAfterStartInput();
}
}
@@ -4226,11 +4242,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
InsetsControlTarget computeImeControlTarget() {
if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
|| (mImeInputTarget != null
- && getImeHostOrFallback(mImeInputTarget.getWindow())
- == mRemoteInsetsControlTarget)) {
+ && getImeHostOrFallback(mImeInputTarget.getWindowState())
+ == mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
- return mImeInputTarget;
+ return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null;
}
}
@@ -4243,7 +4259,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
if (shouldImeAttachedToApp()) {
- if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) {
+ if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
// Do not change parent if the window hasn't requested IME.
return null;
}
@@ -5504,11 +5520,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final float[] tmpFloat9 = new float[9];
forAllWindows(w -> {
if (w.isVisible() && !w.inPinnedWindowingMode()) {
- if (w.mSession.mSetsUnrestrictedKeepClearAreas) {
- outUnrestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
- } else {
- outRestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
- }
+ w.getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
}
// We stop traversing when we reach the base of a fullscreen app.
@@ -5566,13 +5578,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
static boolean alwaysCreateRootTask(int windowingMode, int activityType) {
- // Always create a root task for fullscreen, freeform, and split-screen-secondary windowing
+ // Always create a root task for fullscreen, freeform, and multi windowing
// modes so that we can manage visual ordering and return types correctly.
return activityType == ACTIVITY_TYPE_STANDARD
&& (windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_FREEFORM
|| windowingMode == WINDOWING_MODE_PINNED
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW);
}
@@ -6351,13 +6362,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
- * Return {@code true} if there is an ongoing animation to the "Recents" activity and this
- * activity as a fixed orientation so shouldn't be rotated.
+ * Returns the fixed orientation requested by a transient launch (e.g. recents animation).
+ * If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred.
*/
- boolean isTopFixedOrientationRecentsAnimating() {
- return mAnimatingRecents != null
- && mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */)
- != ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp();
+ @ActivityInfo.ScreenOrientation int getTransientFixedOrientation() {
+ ActivityRecord source = null;
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ final ActivityRecord r = mFixedRotationLaunchingApp;
+ if (r != null && mTransitionController.isTransientLaunch(r)) {
+ source = r;
+ }
+ } else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) {
+ source = mAnimatingRecents;
+ }
+ if (source == null || source.getRequestedConfigurationOrientation(
+ true /* forDisplay */) == ORIENTATION_UNDEFINED) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+ if (!mWmService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
+ // If screen is off or the device is going to sleep, then still allow to update.
+ return SCREEN_ORIENTATION_UNSET;
+ }
+ return source.mOrientation;
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6438d79e5761..f116fffa8075 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -419,11 +419,8 @@ public class DisplayRotation {
* THE SCREEN.
*/
boolean updateRotationUnchecked(boolean forceUpdate) {
- final boolean useShellTransitions =
- mDisplayContent.mTransitionController.isShellTransitionsEnabled();
-
final int displayId = mDisplayContent.getDisplayId();
- if (!forceUpdate && !useShellTransitions) {
+ if (!forceUpdate) {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until updates
// have been resumed.
@@ -449,17 +446,16 @@ public class DisplayRotation {
return false;
}
- final RecentsAnimationController recentsAnimController =
- mService.getRecentsAnimationController();
- if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
- .isTopFixedOrientationRecentsAnimating()
- // If screen is off or the device is going to sleep, then still allow to update.
- && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
+ final int transientFixedOrientation =
+ mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation();
+ if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) {
+ // Makes sure that after the transition is finished, updateOrientation() can see
+ // the difference from the latest orientation source.
+ mLastOrientation = transientFixedOrientation;
// 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
// rotation update while the animation is running.
- recentsAnimController.setCheckRotationAfterCleanup();
return false;
}
}
@@ -513,7 +509,7 @@ public class DisplayRotation {
mDisplayContent.setLayoutNeeded();
- if (useShellTransitions) {
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
index d754fd861e27..684cf06e08b8 100644
--- a/services/core/java/com/android/server/wm/DragResizeMode.java
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -40,8 +40,6 @@ class DragResizeMode {
switch (mode) {
case DRAG_RESIZE_MODE_FREEFORM:
return rootTask.getWindowingMode() == WINDOWING_MODE_FREEFORM;
- case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
- return rootTask.inSplitScreenWindowingMode();
default:
return false;
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 2ab08e6da478..dcc16ebd88f5 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -25,6 +28,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.proto.ProtoOutputStream;
import android.util.Slog;
import android.view.IWindow;
import android.view.InputApplicationHandle;
@@ -43,6 +47,8 @@ class EmbeddedWindowController {
private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
new ArrayMap<>();
+ private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
+ new ArrayMap<>();
private final Object mGlobalLock;
private final ActivityTaskManagerService mAtmService;
@@ -63,6 +69,7 @@ class EmbeddedWindowController {
mWindows.put(inputToken, window);
final IBinder focusToken = window.getFocusGrantToken();
mWindowsByFocusToken.put(focusToken, window);
+ mWindowsByWindowToken.put(window.getWindowToken(), window);
updateProcessController(window);
window.mClient.asBinder().linkToDeath(()-> {
synchronized (mGlobalLock) {
@@ -116,6 +123,7 @@ class EmbeddedWindowController {
if (ew.mClient.asBinder() == client.asBinder()) {
mWindows.removeAt(i).onRemoved();
mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByWindowToken.remove(ew.getWindowToken());
return;
}
}
@@ -127,6 +135,7 @@ class EmbeddedWindowController {
if (ew.mHostWindowState == host) {
mWindows.removeAt(i).onRemoved();
mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByWindowToken.remove(ew.getWindowToken());
}
}
}
@@ -139,6 +148,10 @@ class EmbeddedWindowController {
return mWindowsByFocusToken.get(focusGrantToken);
}
+ EmbeddedWindow getByWindowToken(IBinder windowToken) {
+ return mWindowsByWindowToken.get(windowToken);
+ }
+
void onActivityRemoved(ActivityRecord activityRecord) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
final EmbeddedWindow window = mWindows.valueAt(i);
@@ -244,15 +257,29 @@ class EmbeddedWindowController {
}
@Override
+ public DisplayContent getDisplayContent() {
+ return mWmService.mRoot.getDisplayContent(getDisplayId());
+ }
+
+ @Override
public IWindow getIWindow() {
return mClient;
}
+ public IBinder getWindowToken() {
+ return mClient.asBinder();
+ }
+
@Override
public int getPid() {
return mOwnerPid;
}
+ @Override
+ public int getUid() {
+ return mOwnerUid;
+ }
+
void setIsOverlay() {
mIsOverlay = true;
}
@@ -297,5 +324,46 @@ class EmbeddedWindowController {
public void handleTapOutsideFocusInsideSelf() {
handleTap(true);
}
+
+ @Override
+ public boolean shouldControlIme() {
+ return false;
+ }
+
+ @Override
+ public boolean canScreenshotIme() {
+ return true;
+ }
+
+ @Override
+ public void unfreezeInsetsAfterStartInput() {
+ }
+
+ @Override
+ public InsetsControlTarget getImeControlTarget() {
+ return mWmService.getDefaultDisplayContentLocked().mRemoteInsetsControlTarget;
+ }
+
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ return uid == mOwnerUid && pid == mOwnerPid;
+ }
+
+ @Override
+ public ActivityRecord getActivityRecord() {
+ return null;
+ }
+
+ @Override
+ public void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ final long token = proto.start(fieldId);
+
+ final long token2 = proto.start(IDENTIFIER);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(TITLE, "EmbeddedWindow");
+ proto.end(token2);
+ proto.end(token);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f24e429ca09c..199517c441ad 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -21,7 +21,6 @@ import static android.view.InsetsState.ITYPE_IME;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
@@ -249,7 +248,7 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
}
private boolean isImeInputTarget(InsetsControlTarget target) {
- return target == mDisplayContent.getImeTarget(IME_TARGET_INPUT);
+ return target == mDisplayContent.getImeInputTarget();
}
private boolean sameAsImeControlTarget() {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1f0fdcf0a8d2..8d1425d17d47 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -39,6 +39,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.input.InputManagerService;
import java.io.PrintWriter;
+import java.util.OptionalInt;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
@@ -98,23 +99,14 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
}
@Override
- public void notifyGestureMonitorUnresponsive(int pid, @NonNull String reason) {
- mService.mAnrController.notifyGestureMonitorUnresponsive(pid, reason);
+ public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
+ @NonNull String reason) {
+ mService.mAnrController.notifyWindowUnresponsive(token, pid, reason);
}
@Override
- public void notifyWindowUnresponsive(@NonNull IBinder token, String reason) {
- mService.mAnrController.notifyWindowUnresponsive(token, reason);
- }
-
- @Override
- public void notifyGestureMonitorResponsive(int pid) {
- mService.mAnrController.notifyGestureMonitorResponsive(pid);
- }
-
- @Override
- public void notifyWindowResponsive(@NonNull IBinder token) {
- mService.mAnrController.notifyWindowResponsive(token);
+ public void notifyWindowResponsive(@NonNull IBinder token, @NonNull OptionalInt pid) {
+ mService.mAnrController.notifyWindowResponsive(token, pid);
}
/** Notifies that the input device configuration has changed. */
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index 5166b8adcecc..b5ab62b6e03f 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.view.IWindow;
+import android.util.proto.ProtoOutputStream;
/**
* Common interface between focusable objects.
@@ -36,6 +37,7 @@ interface InputTarget {
/* Owning pid of the target. */
int getPid();
+ int getUid();
/**
* Indicates whether a target should receive focus from server side
@@ -45,7 +47,25 @@ interface InputTarget {
*/
boolean receiveFocusFromTapOutside();
+ // Gaining focus
void handleTapOutsideFocusInsideSelf();
+ // Losing focus
void handleTapOutsideFocusOutsideSelf();
+
+ // Whether this input target can control the IME itself
+ boolean shouldControlIme();
+ // Whether this input target can be screenshoted by the IME system
+ boolean canScreenshotIme();
+
+ ActivityRecord getActivityRecord();
+ void unfreezeInsetsAfterStartInput();
+
+ boolean isInputMethodClientFocus(int uid, int pid);
+
+ DisplayContent getDisplayContent();
+ InsetsControlTarget getImeControlTarget();
+
+ void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel);
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb6d83c230ac..4fdb1f7930f5 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -449,11 +449,8 @@ class InsetsPolicy {
boolean copyState) {
final WindowState roundedCornerWindow = mPolicy.getRoundedCornerWindow();
final Task task = w.getTask();
- final boolean isInSplitScreenMode = task != null && task.inMultiWindowMode()
- && task.getRootTask() != null
- && task.getRootTask().getAdjacentTaskFragment() != null;
if (task != null && !task.getWindowConfiguration().tasksAreFloating()
- && (roundedCornerWindow != null || isInSplitScreenMode)) {
+ && (roundedCornerWindow != null || task.inSplitScreen())) {
// Instead of using display frame to calculating rounded corner, for the fake rounded
// corners drawn by divider bar or task bar, we need to re-calculate rounded corners
// based on task bounds and if the task bounds is intersected with task bar, we should
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 30906e5b3db8..a407021c7594 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
@@ -122,7 +121,6 @@ public class RecentsAnimationController implements DeathRecipient {
private final int mDisplayId;
private boolean mWillFinishToHome = false;
private final Runnable mFailsafeRunnable = this::onFailsafe;
- private Runnable mCheckRotationAfterCleanup;
// The recents component app token that is shown behind the visibile tasks
private ActivityRecord mTargetActivityRecord;
@@ -611,8 +609,7 @@ public class RecentsAnimationController implements DeathRecipient {
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
final Task task = adapter.mTask;
final TaskFragment adjacentTask = task.getRootTask().getAdjacentTaskFragment();
- final boolean inSplitScreen = task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
- && adjacentTask != null;
+ final boolean inSplitScreen = task.inSplitScreen();
if (task.isActivityTypeHomeOrRecents()
// Skip if the task is in split screen and in landscape.
|| (inSplitScreen && isDisplayLandscape)
@@ -920,24 +917,6 @@ public class RecentsAnimationController implements DeathRecipient {
}
/**
- * If the display rotation change is ignored while recents animation is running, make sure that
- * the pending rotation change will be applied after the animation finishes.
- */
- void setCheckRotationAfterCleanup() {
- if (mCheckRotationAfterCleanup != null) return;
- mCheckRotationAfterCleanup = () -> {
- synchronized (mService.mGlobalLock) {
- if (mDisplayContent.getDisplayRotation()
- .updateRotationAndSendNewConfigIfChanged()) {
- if (mTargetActivityRecord != null) {
- mTargetActivityRecord.finishFixedRotationTransform();
- }
- }
- }
- };
- }
-
- /**
* @return Whether we should defer the cancel from a root task order change until the next app
* transition.
*/
@@ -1037,10 +1016,6 @@ public class RecentsAnimationController implements DeathRecipient {
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
}
- if (mCheckRotationAfterCleanup != null) {
- mService.mH.post(mCheckRotationAfterCleanup);
- mCheckRotationAfterCleanup = null;
- }
}
void scheduleFailsafe() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index cd8ddf4e9211..bafdd92c058a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -84,6 +84,7 @@ import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
+import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
@@ -521,10 +522,15 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void reportKeepClearAreasChanged(IWindow window, List<Rect> keepClearAreas) {
+ public void reportKeepClearAreasChanged(IWindow window, List<Rect> restricted,
+ List<Rect> unrestricted) {
+ if (!mSetsUnrestrictedKeepClearAreas && !unrestricted.isEmpty()) {
+ unrestricted = Collections.emptyList();
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
- mService.reportKeepClearAreasChanged(this, window, keepClearAreas);
+ mService.reportKeepClearAreasChanged(this, window, restricted, unrestricted);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc03c60aa6d6..7fe34f47c53c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -1722,6 +1723,13 @@ class Task extends TaskFragment {
&& (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda));
}
+ /** Returns {@code true} if this task is currently in split-screen. */
+ boolean inSplitScreen() {
+ return getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+ && getRootTask() != null
+ && getRootTask().getAdjacentTaskFragment() != null;
+ }
+
private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) {
return super.supportsSplitScreenWindowingMode()
&& mAtmService.mSupportsSplitScreenMultiWindow
@@ -3292,7 +3300,7 @@ class Task extends TaskFragment {
// We intend to let organizer manage task visibility but it doesn't
// have enough information until we finish shell transitions.
// In the mean time we do an easy fix here.
- final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS);
+ final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS | CHILDREN);
if (mSurfaceControl != null) {
if (show != mLastSurfaceShowing) {
getSyncTransaction().setVisibility(mSurfaceControl, show);
@@ -6020,9 +6028,6 @@ class Task extends TaskFragment {
}
boolean shouldIgnoreInput() {
- if (inSplitScreenPrimaryWindowingMode() && !isFocusable()) {
- return true;
- }
if (mAtmService.mHasLeanbackFeature && inPinnedWindowingMode()
&& !isFocusedRootTaskOnDisplay()) {
// Preventing Picture-in-Picture root task from receiving input on TVs.
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 7fab94cab413..afc3087f4ee9 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1767,8 +1767,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
// Resolve override windowing mode to fullscreen for home task (even on freeform
// display), or split-screen if in split-screen mode.
if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
- ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 037d582edc30..331f1242da1d 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -729,6 +729,16 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Skip if task still not appeared.
return;
}
+ if (force && mPendingTaskEvents.isEmpty()) {
+ // There are task-info changed events do not result in
+ // - RootWindowContainer#performSurfacePlacementNoTrace OR
+ // - WindowAnimator#animate
+ // For instance, when an app requesting aspect ratio change when in PiP mode.
+ // To solve this, we directly dispatch the pending event if there are no events queued (
+ // otherwise, all pending events should be dispatched on next drawn).
+ dispatchTaskInfoChanged(task, true /* force */);
+ return;
+ }
// Defer task info reporting while layout is deferred. This is because layout defer
// blocks tend to do lots of re-ordering which can mess up animations in receivers.
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6a23eb588102..cde99271b0ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -534,7 +534,7 @@ class TaskSnapshotController {
// Since RecentsAnimation will handle task snapshot while switching apps with the
// best capture timing (e.g. IME window capture),
// No need additional task capture while task is controlled by RecentsAnimation.
- if (task.isAnimatingByRecents()) {
+ if (isAnimatingByRecents(task)) {
mSkipClosingAppSnapshotTasks.add(task);
}
// If the task of the app is not visible anymore, it means no other app in that task
@@ -686,7 +686,7 @@ class TaskSnapshotController {
// Since RecentsAnimation will handle task snapshot while switching apps with the best
// capture timing (e.g. IME window capture), No need additional task capture while task
// is controlled by RecentsAnimation.
- if (task.isVisible() && !task.isAnimatingByRecents()) {
+ if (task.isVisible() && !isAnimatingByRecents(task)) {
mTmpTasks.add(task);
}
});
@@ -717,6 +717,11 @@ class TaskSnapshotController {
frame, Type.systemBars(), false /* ignoreVisibility */).toRect();
}
+ private boolean isAnimatingByRecents(@NonNull Task task) {
+ return task.isAnimatingByRecents()
+ || mService.mAtmService.getTransitionController().inRecentsTransition(task);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "mHighResTaskSnapshotScale=" + mHighResTaskSnapshotScale);
pw.println(prefix + "mTaskSnapshotEnabled=" + mTaskSnapshotEnabled);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0f5828c7efb0..29d17429fa6a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -80,9 +80,12 @@ import android.window.TransitionInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -548,18 +551,28 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = 0; i < mTargetDisplays.size(); ++i) {
final DisplayContent dc = mTargetDisplays.get(i);
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
- if (asyncRotationController != null) {
+ if (asyncRotationController != null && mTargets.contains(dc)) {
asyncRotationController.onTransitionFinished();
}
if (mTransientLaunches != null) {
+ InsetsControlTarget prevImeTarget = dc.getImeTarget(
+ DisplayContent.IME_TARGET_CONTROL);
+ InsetsControlTarget newImeTarget = null;
// Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
// so re-compute in case the IME target is changed after transition.
for (int t = 0; t < mTransientLaunches.size(); ++t) {
if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
- dc.computeImeTarget(true /* updateImeTarget */);
+ newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
break;
}
}
+ if (mRecentsDisplayId != INVALID_DISPLAY && prevImeTarget == newImeTarget) {
+ // Restore IME icon only when moving the original app task to front from
+ // recents, in case IME icon may missing if the moving task has already been
+ // the current focused task.
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ false /* disableImeIcon */);
+ }
}
dc.handleCompleteDeferredRemoval();
}
@@ -696,7 +709,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
final AsyncRotationController controller = dc.getAsyncRotationController();
- if (controller != null) {
+ if (controller != null && mTargets.contains(dc)) {
controller.setupStartTransaction(transaction);
}
mStartTransaction = transaction;
@@ -781,6 +794,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
+ // Hiding IME/IME icon when starting quick-step with resents animation.
+ if (!mTargetDisplays.get(mRecentsDisplayId).isImeAttachedToApp()) {
+ // Hiding IME if IME window is not attached to app.
+ // Since some windowing mode is not proper to snapshot Task with IME window
+ // while the app transitioning to the next task (e.g. split-screen mode)
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (inputMethodManagerInternal != null) {
+ inputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
+ }
+ } else {
+ // Disable IME icon explicitly when IME attached to the app in case
+ // IME icon might flickering while swiping to the next app task still
+ // in animating before the next app window focused, or IME icon
+ // persists on the bottom when swiping the task to recents.
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ true /* disableImeIcon */);
+ }
+
// The rest of this function handles nav-bar reparenting
if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index b0532c2a1552..043623392546 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
@@ -246,7 +245,7 @@ class TransitionController {
/** @return {@code true} if wc is in a participant subtree */
boolean inTransition(@NonNull WindowContainer wc) {
- if (isCollecting(wc)) return true;
+ if (isCollecting(wc)) return true;
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
for (WindowContainer p = wc; p != null; p = p.getParent()) {
if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
@@ -257,6 +256,28 @@ class TransitionController {
return false;
}
+ boolean inRecentsTransition(@NonNull WindowContainer wc) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ // TODO(b/221417431): replace this with deterministic snapshots
+ if (mCollectingTransition == null) break;
+ if ((mCollectingTransition.getFlags() & TRANSIT_FLAG_IS_RECENTS) != 0
+ && mCollectingTransition.mParticipants.contains(wc)) {
+ return true;
+ }
+ }
+
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ // TODO(b/221417431): replace this with deterministic snapshots
+ if ((mPlayingTransitions.get(i).getFlags() & TRANSIT_FLAG_IS_RECENTS) != 0
+ && mPlayingTransitions.get(i).mParticipants.contains(p)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/** @return {@code true} if wc is in a participant subtree */
boolean isTransitionOnDisplay(@NonNull DisplayContent dc) {
if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) {
@@ -514,16 +535,16 @@ class TransitionController {
// 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) {
+ if (activity.isActivityTypeHomeOrRecents()) {
mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ // When starting recents animation, we assume the recents activity is behind the app
+ // task and should not affect system bar appearance,
+ // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
+ // the gesture threshold.
+ activity.getTask().setCanAffectSystemUiFlags(false);
}
}
- void setSeamlessRotation(@NonNull WindowContainer wc) {
- if (mCollectingTransition == null) return;
- mCollectingTransition.setSeamlessRotation(wc);
- }
-
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
final Transition transition = Transition.fromBinder(token);
if (transition == null || !mPlayingTransitions.contains(transition)) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a67f92332d8e..56014adb705d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -47,6 +47,7 @@ import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -1023,7 +1024,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mProvidedInsetsSources;
}
- DisplayContent getDisplayContent() {
+ public DisplayContent getDisplayContent() {
return mDisplayContent;
}
@@ -1173,6 +1174,27 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mTransitionController.inTransition(this);
}
+ boolean inAppOrRecentsTransition() {
+ if (!mTransitionController.isShellTransitionsEnabled()) {
+ return isAnimating(PARENTS | TRANSITION,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ }
+ for (WindowContainer p = this; p != null; p = p.getParent()) {
+ if (mTransitionController.isCollecting(p)) {
+ return true;
+ }
+ }
+ if (inTransition() || mTransitionController.inRecentsTransition(this)) return true;
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ WindowContainer child = mChildren.get(i);
+ if (child.inAppOrRecentsTransition()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void sendAppVisibilityToClients() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5db72ee722d7..03e21405ff03 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -114,7 +115,6 @@ import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
@@ -3035,7 +3035,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onKeyguardShowingAndNotOccludedChanged() {
mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
- dispatchKeyguardLockedStateState();
+ dispatchKeyguardLockedState();
}
@Override
@@ -3249,7 +3249,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " permission required to read keyguard visibility");
}
- private void dispatchKeyguardLockedStateState() {
+ private void dispatchKeyguardLockedState() {
mH.post(() -> {
final boolean isKeyguardLocked = mPolicy.isKeyguardShowing();
if (mDispatchedKeyguardLockedState == isKeyguardLocked) {
@@ -4412,10 +4412,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void reportKeepClearAreasChanged(Session session, IWindow window, List<Rect> keepClearAreas) {
+ void reportKeepClearAreasChanged(Session session, IWindow window,
+ List<Rect> restricted, List<Rect> unrestricted) {
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, true);
- if (win.setKeepClearAreas(keepClearAreas)) {
+ if (win.setKeepClearAreas(restricted, unrestricted)) {
win.getDisplayContent().updateKeepClearAreas();
}
}
@@ -5069,6 +5070,15 @@ public class WindowManagerService extends IWindowManager.Stub
return null;
}
+ @Nullable InputTarget getInputTargetFromWindowTokenLocked(IBinder windowToken) {
+ InputTarget window = mWindowMap.get(windowToken);
+ if (window != null) {
+ return window;
+ }
+ window = mEmbeddedWindowController.getByWindowToken(windowToken);
+ return window;
+ }
+
void reportFocusChanged(IBinder oldToken, IBinder newToken) {
InputTarget lastTarget;
InputTarget newTarget;
@@ -6506,7 +6516,7 @@ public class WindowManagerService extends IWindowManager.Stub
mRoot.forAllDisplays(dc -> {
final int displayId = dc.getDisplayId();
final InsetsControlTarget imeLayeringTarget = dc.getImeTarget(IME_TARGET_LAYERING);
- final InsetsControlTarget imeInputTarget = dc.getImeTarget(IME_TARGET_INPUT);
+ final InputTarget imeInputTarget = dc.getImeInputTarget();
final InsetsControlTarget imeControlTarget = dc.getImeTarget(IME_TARGET_CONTROL);
if (imeLayeringTarget != null) {
pw.print(" imeLayeringTarget in display# "); pw.print(displayId);
@@ -7714,7 +7724,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ " imeTargetWindowToken=" + imeTargetWindowToken);
}
synchronized (mGlobalLock) {
- final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ InputTarget imeTarget =
+ getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget != null) {
imeTarget.getDisplayContent().updateImeInputAndControlTarget(imeTarget);
}
@@ -7794,11 +7805,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
- final WindowState window = mWindowMap.get(windowToken);
- if (window == null) {
+ InputTarget target = getInputTargetFromWindowTokenLocked(windowToken);
+ if (target == null) {
return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
- final int tokenDisplayId = window.getDisplayContent().getDisplayId();
+ final int tokenDisplayId = target.getDisplayContent().getDisplayId();
if (tokenDisplayId != displayId) {
Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+ " from client: " + displayId
@@ -7811,7 +7822,7 @@ public class WindowManagerService extends IWindowManager.Stub
return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
- if (displayContent.isInputMethodClientFocus(uid, pid)) {
+ if (target.isInputMethodClientFocus(uid, pid)) {
return ImeClientFocusResult.HAS_IME_FOCUS;
}
// Okay, how about this... what is the current focus?
@@ -7835,7 +7846,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void showImePostLayout(IBinder imeTargetWindowToken) {
synchronized (mGlobalLock) {
- WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget == null) {
return;
}
@@ -8974,4 +8985,24 @@ public class WindowManagerService extends IWindowManager.Stub
return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(),
taskSnapshot.getColorSpace());
}
+
+ @Override
+ public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
+ if (!checkCallingPermission(START_TASKS_FROM_RECENTS, "setRecentsAppBehindSystemBars()")) {
+ throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final Task recentsApp = mRoot.getTask(task -> task.isActivityTypeHomeOrRecents()
+ && task.getTopVisibleActivity() != null);
+ if (recentsApp != null) {
+ recentsApp.getTask().setCanAffectSystemUiFlags(behindSystemBars);
+ mWindowPlacerLocked.requestTraversal();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c70a40c3b5fc..ce27d739e1b1 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -21,11 +21,13 @@ import static android.app.ActivityManager.isStartResultSuccessful;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
@@ -895,6 +897,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt);
break;
}
+ case HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER:
+ final Rect insetsProviderWindowContainer = hop.getInsetsProviderFrame();
+ final WindowContainer receiverWindowContainer =
+ WindowContainer.fromBinder(hop.getContainer());
+ receiverWindowContainer.addLocalRectInsetsSourceProvider(
+ insetsProviderWindowContainer, hop.getInsetsTypes());
+ break;
+ case HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER:
+ WindowContainer.fromBinder(hop.getContainer())
+ .removeLocalInsetsSourceProvider(hop.getInsetsTypes());
+ break;
}
return effects;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 26acf43cf9ed..d547275dcf0c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -22,7 +22,6 @@ import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
@@ -120,7 +119,6 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.wm.AnimationSpecProto.MOVE;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.DisplayContent.logsGestureExclusionRestrictions;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
@@ -182,6 +180,7 @@ import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
import static com.android.server.wm.WindowStateProto.STACK_ID;
import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
+import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
@@ -483,6 +482,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
private final List<Rect> mKeepClearAreas = new ArrayList<>();
+ /**
+ * Like mKeepClearAreas, but the unrestricted ones can be trusted to behave nicely.
+ * Floating windows (like Pip) will be moved away from them without applying restrictions.
+ */
+ private final List<Rect> mUnrestrictedKeepClearAreas = new ArrayList<>();
+
// 0 = left, 1 = right
private final int[] mLastRequestedExclusionHeight = {0, 0};
private final int[] mLastGrantedExclusionHeight = {0, 0};
@@ -1024,51 +1029,86 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
/**
- * @return a list of rects that should ideally not be covered by floating windows like pip.
- * The returned rect coordinates are relative to the display origin.
+ * Collects all restricted and unrestricted keep-clear areas for this window.
+ * Keep-clear areas are rects that should ideally not be covered by floating windows like Pip.
+ * The system is more careful about restricted ones and may apply restrictions to them, while
+ * the unrestricted ones are considered safe.
+ *
+ * @param outRestricted list to add restricted keep-clear areas to
+ * @param outUnrestricted list to add unrestricted keep-clear areas to
*/
- List<Rect> getKeepClearAreas() {
+ void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) {
final Matrix tmpMatrix = new Matrix();
final float[] tmpFloat9 = new float[9];
- return getKeepClearAreas(tmpMatrix, tmpFloat9);
+ getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
}
/**
+ * Collects all restricted and unrestricted keep-clear areas for this window.
+ * Keep-clear areas are rects that should ideally not be covered by floating windows like Pip.
+ * The system is more careful about restricted ones and may apply restrictions to them, while
+ * the unrestricted ones are considered safe.
+ *
+ * @param outRestricted list to add restricted keep-clear areas to
+ * @param outUnrestricted list to add unrestricted keep-clear areas to
* @param tmpMatrix a temporary matrix to be used for transformations
* @param float9 a temporary array of 9 floats
- *
- * @return a list of rects that should ideally not be covered by floating windows like pip.
- * The returned rect coordinates are relative to the display origin.
*/
- List<Rect> getKeepClearAreas(Matrix tmpMatrix, float[] float9) {
+ void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted, Matrix tmpMatrix,
+ float[] float9) {
+ outRestricted.addAll(getRectsInScreenSpace(mKeepClearAreas, tmpMatrix, float9));
+ outUnrestricted.addAll(
+ getRectsInScreenSpace(mUnrestrictedKeepClearAreas, tmpMatrix, float9));
+ }
+
+ /**
+ * Transforms the given rects from window coordinate space to screen space.
+ */
+ List<Rect> getRectsInScreenSpace(List<Rect> rects, Matrix tmpMatrix, float[] float9) {
getTransformationMatrix(float9, tmpMatrix);
- // Translate all keep-clear rects to screen coordinates.
- final List<Rect> transformedKeepClearAreas = new ArrayList<Rect>();
+ final List<Rect> transformedRects = new ArrayList<Rect>();
final RectF tmpRect = new RectF();
Rect curr;
- for (Rect r : mKeepClearAreas) {
+ for (Rect r : rects) {
tmpRect.set(r);
tmpMatrix.mapRect(tmpRect);
curr = new Rect();
tmpRect.roundOut(curr);
- transformedKeepClearAreas.add(curr);
+ transformedRects.add(curr);
}
- return transformedKeepClearAreas;
+ return transformedRects;
}
/**
- * @param keepClearAreas the new keep-clear areas for this window. The rects should be defined
- * in window coordinate space
+ * Sets the new keep-clear areas for this window. The rects should be defined in window
+ * coordinate space.
+ * Keep-clear areas can be restricted or unrestricted, depending on whether the app holds the
+ * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS} system permission.
+ * Restricted ones will be handled more carefully by the system. Restrictions may be applied.
+ * Unrestricted ones are considered safe. The system should move floating windows away from them
+ * without applying restrictions.
+ *
+ * @param restricted the new restricted keep-clear areas for this window
+ * @param unrestricted the new unrestricted keep-clear areas for this window
*
* @return true if there is a change in the list of keep-clear areas; false otherwise
*/
- boolean setKeepClearAreas(List<Rect> keepClearAreas) {
- if (mKeepClearAreas.equals(keepClearAreas)) {
+ boolean setKeepClearAreas(List<Rect> restricted, List<Rect> unrestricted) {
+ final boolean newRestrictedAreas = !mKeepClearAreas.equals(restricted);
+ final boolean newUnrestrictedAreas = !mUnrestrictedKeepClearAreas.equals(unrestricted);
+ if (!newRestrictedAreas && !newUnrestrictedAreas) {
return false;
}
- mKeepClearAreas.clear();
- mKeepClearAreas.addAll(keepClearAreas);
+ if (newRestrictedAreas) {
+ mKeepClearAreas.clear();
+ mKeepClearAreas.addAll(restricted);
+ }
+
+ if (newUnrestrictedAreas) {
+ mUnrestrictedKeepClearAreas.clear();
+ mUnrestrictedKeepClearAreas.addAll(unrestricted);
+ }
return true;
}
@@ -1602,14 +1642,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- DisplayContent getDisplayContent() {
+ public DisplayContent getDisplayContent() {
return mToken.getDisplayContent();
}
@Override
void onDisplayChanged(DisplayContent dc) {
if (dc != null && mDisplayContent != null && dc != mDisplayContent
- && getImeInputTarget() == this) {
+ && mDisplayContent.getImeInputTarget() == this) {
dc.updateImeInputAndControlTarget(getImeInputTarget());
mDisplayContent.setImeInputTarget(null);
}
@@ -1749,6 +1789,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mSession.mPid;
}
+ @Override
+ public int getUid() {
+ return mSession.mUid;
+ }
+
Task getTask() {
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
@@ -2348,7 +2393,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- if (getDisplayContent().getImeTarget(IME_TARGET_INPUT) != this && !isImeLayeringTarget()) {
+ if (getDisplayContent().getImeInputTarget() != this && !isImeLayeringTarget()) {
super.onConfigurationChanged(newParentConfig);
return;
}
@@ -2424,7 +2469,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
dc.setImeLayeringTarget(null);
dc.computeImeTarget(true /* updateImeTarget */);
}
- if (dc.getImeTarget(IME_TARGET_INPUT) == this) {
+ if (dc.getImeInputTarget() == this) {
dc.updateImeInputAndControlTarget(null);
}
@@ -3578,11 +3623,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int requested = mLastRequestedExclusionHeight[side];
final int granted = mLastGrantedExclusionHeight[side];
+ final boolean inSplitScreen = getTask() != null && getTask().inSplitScreen();
+
FrameworkStatsLog.write(FrameworkStatsLog.EXCLUSION_RECT_STATE_CHANGED,
mAttrs.packageName, requested, requested - granted /* rejected */,
side + 1 /* Sides are 1-indexed in atoms.proto */,
(getConfiguration().orientation == ORIENTATION_LANDSCAPE),
- isSplitScreenWindowingMode(getWindowingMode()), (int) duration);
+ inSplitScreen, (int) duration);
}
private void initExclusionRestrictions() {
@@ -4117,8 +4164,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (task == null) {
return false;
}
- if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()
- && !task.getRootTask().mCreatedByOrganizer) {
+ if (!inFreeformWindowingMode() && !task.getRootTask().mCreatedByOrganizer) {
return false;
}
// TODO(157912944): formalize drag-resizing so that exceptions aren't hardcoded like this
@@ -4198,9 +4244,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
- for (Rect r : getKeepClearAreas()) {
+ for (Rect r : mKeepClearAreas) {
r.dumpDebug(proto, KEEP_CLEAR_AREAS);
}
+ for (Rect r : mUnrestrictedKeepClearAreas) {
+ r.dumpDebug(proto, UNRESTRICTED_KEEP_CLEAR_AREAS);
+ }
proto.end(token);
}
@@ -4369,7 +4418,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
pw.println(prefix + "isOnScreen=" + isOnScreen());
pw.println(prefix + "isVisible=" + isVisible());
- pw.println(prefix + "keepClearAreas=" + getKeepClearAreas());
+ pw.println(prefix + "keepClearAreas: restricted=" + mKeepClearAreas
+ + ", unrestricted=" + mUnrestrictedKeepClearAreas);
if (dumpAll) {
final String visibilityString = mRequestedVisibilities.toString();
if (!visibilityString.isEmpty()) {
@@ -5596,7 +5646,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @return {@link InsetsControlTarget} of host that controls the IME.
* When window is doesn't have a parent, it is returned as-is.
*/
- InsetsControlTarget getImeControlTarget() {
+ @Override
+ public InsetsControlTarget getImeControlTarget() {
return getDisplayContent().getImeHostOrFallback(this);
}
@@ -5731,8 +5782,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
WindowState getImeInputTarget() {
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_INPUT);
- return target != null ? target.getWindow() : null;
+ final InputTarget target = mDisplayContent.getImeInputTarget();
+ return target != null ? target.getWindowState() : null;
}
void forceReportingResized() {
@@ -6117,4 +6168,37 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
mGivenTouchableRegion.setEmpty();
}
+
+ @Override
+ public boolean shouldControlIme() {
+ return !inMultiWindowMode();
+ }
+
+ @Override
+ public boolean canScreenshotIme() {
+ return !isSecureLocked();
+ }
+
+ @Override
+ public ActivityRecord getActivityRecord() {
+ return mActivityRecord;
+ }
+
+ @Override
+ public void unfreezeInsetsAfterStartInput() {
+ if (mActivityRecord != null) {
+ mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ }
+ }
+
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ return getDisplayContent().isInputMethodClientFocus(uid, pid);
+ }
+
+ @Override
+ public void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ dumpDebug(proto, fieldId, logLevel);
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3c122b03d0d1..31b557949e36 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -96,8 +96,6 @@ static struct {
jmethodID notifyNoFocusedWindowAnr;
jmethodID notifyWindowUnresponsive;
jmethodID notifyWindowResponsive;
- jmethodID notifyMonitorUnresponsive;
- jmethodID notifyMonitorResponsive;
jmethodID notifyFocusChanged;
jmethodID notifySensorEvent;
jmethodID notifySensorAccuracy;
@@ -308,10 +306,9 @@ public:
void notifyConfigurationChanged(nsecs_t when) override;
// ANR-related callbacks -- start
void notifyNoFocusedWindowAnr(const std::shared_ptr<InputApplicationHandle>& handle) override;
- void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) override;
- void notifyWindowResponsive(const sp<IBinder>& token) override;
- void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override;
- void notifyMonitorResponsive(int32_t pid) override;
+ void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+ const std::string& reason) override;
+ void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) override;
// ANR-related callbacks -- end
void notifyInputChannelBroken(const sp<IBinder>& token) override;
void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
@@ -838,6 +835,7 @@ void NativeInputManager::notifyNoFocusedWindowAnr(
}
void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,
+ std::optional<int32_t> pid,
const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyWindowUnresponsive");
@@ -851,11 +849,12 @@ void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,
ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowUnresponsive, tokenObj,
- reasonObj.get());
+ pid.value_or(0), pid.has_value(), reasonObj.get());
checkAndClearExceptionFromCallback(env, "notifyWindowUnresponsive");
}
-void NativeInputManager::notifyWindowResponsive(const sp<IBinder>& token) {
+void NativeInputManager::notifyWindowResponsive(const sp<IBinder>& token,
+ std::optional<int32_t> pid) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyWindowResponsive");
#endif
@@ -866,39 +865,11 @@ void NativeInputManager::notifyWindowResponsive(const sp<IBinder>& token) {
jobject tokenObj = javaObjectForIBinder(env, token);
- env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowResponsive, tokenObj);
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowResponsive, tokenObj,
+ pid.value_or(0), pid.has_value());
checkAndClearExceptionFromCallback(env, "notifyWindowResponsive");
}
-void NativeInputManager::notifyMonitorUnresponsive(int32_t pid, const std::string& reason) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- ALOGD("notifyMonitorUnresponsive");
-#endif
- ATRACE_CALL();
-
- JNIEnv* env = jniEnv();
- ScopedLocalFrame localFrame(env);
-
- ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));
-
- env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyMonitorUnresponsive, pid,
- reasonObj.get());
- checkAndClearExceptionFromCallback(env, "notifyMonitorUnresponsive");
-}
-
-void NativeInputManager::notifyMonitorResponsive(int32_t pid) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- ALOGD("notifyMonitorResponsive");
-#endif
- ATRACE_CALL();
-
- JNIEnv* env = jniEnv();
- ScopedLocalFrame localFrame(env);
-
- env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyMonitorResponsive, pid);
- checkAndClearExceptionFromCallback(env, "notifyMonitorResponsive");
-}
-
void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyInputChannelBroken");
@@ -2506,16 +2477,10 @@ int register_android_server_InputManager(JNIEnv* env) {
"(Landroid/view/InputApplicationHandle;)V");
GET_METHOD_ID(gServiceClassInfo.notifyWindowUnresponsive, clazz, "notifyWindowUnresponsive",
- "(Landroid/os/IBinder;Ljava/lang/String;)V");
-
- GET_METHOD_ID(gServiceClassInfo.notifyMonitorUnresponsive, clazz, "notifyMonitorUnresponsive",
- "(ILjava/lang/String;)V");
+ "(Landroid/os/IBinder;IZLjava/lang/String;)V");
GET_METHOD_ID(gServiceClassInfo.notifyWindowResponsive, clazz, "notifyWindowResponsive",
- "(Landroid/os/IBinder;)V");
-
- GET_METHOD_ID(gServiceClassInfo.notifyMonitorResponsive, clazz, "notifyMonitorResponsive",
- "(I)V");
+ "(Landroid/os/IBinder;IZ)V");
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ef61fbfa53ac..5cda9ea66999 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -214,6 +214,7 @@ import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
+import android.app.admin.WifiSsidPolicy;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
@@ -269,6 +270,7 @@ import android.net.Uri;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -389,6 +391,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -8607,20 +8610,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
admin.getPackageName(), userId, "set-device-owner");
Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
+ }
- if (setProfileOwnerOnCurrentUserIfNecessary
- && mInjector.userManagerIsHeadlessSystemUserMode()) {
- int currentForegroundUser = getCurrentForegroundUserId();
- Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
- + " as profile owner on user " + currentForegroundUser);
- // Sets profile owner on current foreground user since
- // the human user will complete the DO setup workflow from there.
- manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
- /* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
- /* showDisclaimer= */ false);
+ if (setProfileOwnerOnCurrentUserIfNecessary
+ && mInjector.userManagerIsHeadlessSystemUserMode()) {
+ int currentForegroundUser;
+ synchronized (getLockObject()) {
+ currentForegroundUser = getCurrentForegroundUserId();
}
- return true;
+ Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+ + " as profile owner on user " + currentForegroundUser);
+ // Sets profile owner on current foreground user since
+ // the human user will complete the DO setup workflow from there.
+ manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
+ /* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
+ /* showDisclaimer= */ false);
}
+ return true;
}
@Override
@@ -10850,7 +10856,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userHandle = user.getIdentifier();
final long id = mInjector.binderClearCallingIdentity();
try {
- maybeInstallDeviceManagerRoleHolderInUser(userHandle);
+ maybeInstallDevicePolicyManagementRoleHolderInUser(userHandle);
manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
/* showDisclaimer= */ true);
@@ -17729,7 +17735,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
startTime,
callerPackage);
- maybeInstallDeviceManagerRoleHolderInUser(userInfo.id);
+ maybeInstallDevicePolicyManagementRoleHolderInUser(userInfo.id);
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
@@ -17797,24 +17803,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void onCreateAndProvisionManagedProfileCompleted(
ManagedProfileProvisioningParams provisioningParams) {}
- private void maybeInstallDeviceManagerRoleHolderInUser(int targetUserId) {
- String deviceManagerRoleHolderPackageName = getDeviceManagerRoleHolderPackageName(mContext);
- if (deviceManagerRoleHolderPackageName == null) {
- Slogf.d(LOG_TAG, "No device manager role holder specified.");
+ private void maybeInstallDevicePolicyManagementRoleHolderInUser(int targetUserId) {
+ String devicePolicyManagerRoleHolderPackageName =
+ getDevicePolicyManagementRoleHolderPackageName(mContext);
+ if (devicePolicyManagerRoleHolderPackageName == null) {
+ Slogf.d(LOG_TAG, "No device policy management role holder specified.");
return;
}
try {
if (mIPackageManager.isPackageAvailable(
- deviceManagerRoleHolderPackageName, targetUserId)) {
- Slogf.d(LOG_TAG, "The device manager role holder "
- + deviceManagerRoleHolderPackageName + " is already installed in "
+ devicePolicyManagerRoleHolderPackageName, targetUserId)) {
+ Slogf.d(LOG_TAG, "The device policy management role holder "
+ + devicePolicyManagerRoleHolderPackageName + " is already installed in "
+ "user " + targetUserId);
return;
}
- Slogf.d(LOG_TAG, "Installing the device manager role holder "
- + deviceManagerRoleHolderPackageName + " in user " + targetUserId);
+ Slogf.d(LOG_TAG, "Installing the device policy management role holder "
+ + devicePolicyManagerRoleHolderPackageName + " in user " + targetUserId);
mIPackageManager.installExistingPackageAsUser(
- deviceManagerRoleHolderPackageName,
+ devicePolicyManagerRoleHolderPackageName,
targetUserId,
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_POLICY,
@@ -17824,10 +17831,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private String getDeviceManagerRoleHolderPackageName(Context context) {
+ private String getDevicePolicyManagementRoleHolderPackageName(Context context) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
List<String> roleHolders =
- roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER);
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
if (roleHolders.isEmpty()) {
return null;
}
@@ -18514,9 +18521,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
);
}
- private void validateCurrentWifiMeetsAdminRequirements() {
+ private void notifyMinimumRequiredWifiSecurityLevelChanged(int level) {
mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements());
+ () -> mInjector.getWifiManager()
+ .notifyMinimumRequiredWifiSecurityLevelChanged(level));
+ }
+
+ private void notifyWifiSsidPolicyChanged(int policyType, List<String> ssids) {
+ List<WifiSsid> wifiSsidList = new ArrayList<>();
+ for (String ssid : ssids) {
+ wifiSsidList.add(
+ WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8)));
+ }
+ WifiSsidPolicy policy = new WifiSsidPolicy(policyType, new ArraySet<>(wifiSsidList));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getWifiManager().notifyWifiSsidPolicyChanged(policy));
}
@Override
@@ -18536,7 +18555,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
valueChanged = true;
}
}
- if (valueChanged) validateCurrentWifiMeetsAdminRequirements();
+ if (valueChanged) notifyMinimumRequiredWifiSecurityLevelChanged(level);
}
@Override
@@ -18568,7 +18587,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (changed) saveSettingsLocked(caller.getUserId());
}
- if (changed) validateCurrentWifiMeetsAdminRequirements();
+ if (changed && !ssids.isEmpty()) {
+ notifyWifiSsidPolicyChanged(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+ }
}
@Override
@@ -18607,7 +18628,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
if (changed) saveSettingsLocked(caller.getUserId());
}
- if (changed) validateCurrentWifiMeetsAdminRequirements();
+ if (changed) {
+ notifyWifiSsidPolicyChanged(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
+ }
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index 598f9e88ac6d..f3b164c6501c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -94,7 +94,7 @@ public class OverlayPackagesProvider {
String getActiveApexPackageNameContainingPackage(String packageName);
- String getDeviceManagerRoleHolderPackageName(Context context);
+ String getDevicePolicyManagementRoleHolderPackageName(Context context);
}
private static final class DefaultInjector implements Injector {
@@ -110,11 +110,11 @@ public class OverlayPackagesProvider {
}
@Override
- public String getDeviceManagerRoleHolderPackageName(Context context) {
+ public String getDevicePolicyManagementRoleHolderPackageName(Context context) {
return Binder.withCleanCallingIdentity(() -> {
RoleManager roleManager = context.getSystemService(RoleManager.class);
List<String> roleHolders =
- roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER);
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
if (roleHolders.isEmpty()) {
return null;
}
@@ -166,7 +166,7 @@ public class OverlayPackagesProvider {
private Set<String> getDeviceManagerRoleHolders() {
HashSet<String> result = new HashSet<>();
String deviceManagerRoleHolderPackageName =
- mInjector.getDeviceManagerRoleHolderPackageName(mContext);
+ mInjector.getDevicePolicyManagementRoleHolderPackageName(mContext);
if (deviceManagerRoleHolderPackageName != null) {
result.add(deviceManagerRoleHolderPackageName);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a14e14b22684..4ff53ead700e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -422,8 +422,8 @@ public final class SystemServer implements Dumpable {
private static final String SAFETY_CENTER_SERVICE_CLASS =
"com.android.safetycenter.SafetyCenterService";
- private static final String SUPPLEMENTALPROCESS_SERVICE_CLASS =
- "com.android.server.supplementalprocess.SupplementalProcessManagerService$Lifecycle";
+ private static final String SDK_SANDBOX_MANAGER_SERVICE_CLASS =
+ "com.android.server.sdksandbox.SdkSandboxManagerService$Lifecycle";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -2605,9 +2605,9 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(IncidentCompanionService.class);
t.traceEnd();
- // Supplemental Process
- t.traceBegin("StartSupplementalProcessManagerService");
- mSystemServiceManager.startService(SUPPLEMENTALPROCESS_SERVICE_CLASS);
+ // SdkSandboxManagerService
+ t.traceBegin("StarSdkSandboxManagerService");
+ mSystemServiceManager.startService(SDK_SANDBOX_MANAGER_SERVICE_CLASS);
t.traceEnd();
if (safeMode) {
diff --git a/services/proguard.flags b/services/proguard.flags
index 0e081f182d0d..425da6c11177 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -40,9 +40,15 @@
# Global entities normally kept through explicit Manifest entries
# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/AndroidManifest.xml,
# by including that manifest with the library rule that triggers optimization.
--keep,allowoptimization,allowaccessmodification class * extends android.app.backup.BackupAgent
--keep,allowoptimization,allowaccessmodification class * extends android.content.BroadcastReceiver
--keep,allowoptimization,allowaccessmodification class * extends android.content.ContentProvider
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Activity
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Service
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.backup.BackupAgent
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.BroadcastReceiver
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.ContentProvider
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.preference.Preference
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.view.View {
+ public <init>(...);
+}
# Various classes subclassed in or referenced via JNI in ethernet-service
-keep public class android.net.** { *; }
@@ -67,6 +73,7 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssConfiguration$HalInterfaceVersion { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; }
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
index b541512fdc9e..f5fea1bed445 100644
--- a/services/tests/inprocesstests/AndroidTest.xml
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -18,6 +18,8 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="FrameworksInProcessTests.apk"/>
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 3e60af33e408..670c1596e15e 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -52,7 +52,7 @@ android_test {
"service-blobstore",
"service-jobscheduler",
"service-permission.impl",
- "service-supplementalprocess.impl",
+ "service-sdksandbox.impl",
"services.companion",
"services.core",
"services.devicepolicy",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 26b5218d2ab4..4a40b5f2de7b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -1000,7 +1000,7 @@ public class ApplicationExitInfoTest {
final String dummyPackageName = "com.android.test";
final String dummyClassName = ".Foo";
app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName(
- dummyPackageName, dummyClassName), "", definingUid));
+ dummyPackageName, dummyClassName), "", definingUid, ""));
}
app.mServices.setConnectionGroup(connectionGroup);
app.mState.setReportedProcState(procState);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index a0ac50634a7b..9a4f8e261124 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -16,11 +16,6 @@
package com.android.server.job.controllers;
-import static android.app.job.JobInfo.PRIORITY_DEFAULT;
-import static android.app.job.JobInfo.PRIORITY_HIGH;
-import static android.app.job.JobInfo.PRIORITY_LOW;
-import static android.app.job.JobInfo.PRIORITY_MIN;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -275,14 +270,14 @@ public class QuotaControllerTest {
}
private void setCharging() {
- when(mJobSchedulerService.isBatteryCharging()).thenReturn(true);
+ doReturn(true).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
}
private void setDischarging() {
- when(mJobSchedulerService.isBatteryCharging()).thenReturn(false);
+ doReturn(false).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
@@ -415,14 +410,6 @@ public class QuotaControllerTest {
}
}
- private void setDeviceConfigFloat(String key, float val) {
- mDeviceConfigPropertiesBuilder.setFloat(key, val);
- synchronized (mQuotaController.mLock) {
- mQuotaController.prepareForUpdatedConstantsLocked();
- mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- }
- }
-
private void waitForNonDelayedMessagesProcessed() {
mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
}
@@ -861,7 +848,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
assertEquals(expectedStats, inputStats);
assertTrue(mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
}
assertTrue("Job not ready: " + jobStatus, jobStatus.isReady());
}
@@ -885,7 +872,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
}
// Quota should be exceeded due to activity in active timer.
@@ -910,7 +897,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
}
}
@@ -1508,7 +1495,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1541,7 +1528,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
setStandbyBucket(FREQUENT_INDEX);
@@ -1551,7 +1538,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
setStandbyBucket(WORKING_INDEX);
@@ -1561,7 +1548,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
@@ -1573,7 +1560,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1597,7 +1584,7 @@ public class QuotaControllerTest {
// Max time will phase out, so should use bucket limit.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1613,7 +1600,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1630,7 +1617,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1663,7 +1650,7 @@ public class QuotaControllerTest {
// window time.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1690,107 +1677,7 @@ public class QuotaControllerTest {
// Max time only has one minute phase out. Bucket time has 2 minute phase out.
assertEquals(9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- }
- }
-
- /**
- * Test getTimeUntilQuotaConsumedLocked when the determination is based on the job's priority.
- */
- @Test
- public void testGetTimeUntilQuotaConsumedLocked_Priority() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- // Close to RARE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
- 150 * SECOND_IN_MILLIS, 5), false);
- // Far away from FREQUENT boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), false);
- // Overlap WORKING_SET boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
- 2 * MINUTE_IN_MILLIS, 5), false);
- // Close to ACTIVE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-
- setStandbyBucket(RARE_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(30 * SECOND_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- setStandbyBucket(FREQUENT_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(30 * SECOND_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- setStandbyBucket(WORKING_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(6 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(4 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(2 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
- setStandbyBucket(ACTIVE_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
}
@@ -1820,7 +1707,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1842,7 +1729,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1854,7 +1741,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1867,7 +1754,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1882,15 +1769,15 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@Test
public void testIsWithinQuotaLocked_NeverApp() {
synchronized (mQuotaController.mLock) {
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.never", NEVER_INDEX, PRIORITY_DEFAULT));
+ assertFalse(
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
}
}
@@ -1898,8 +1785,7 @@ public class QuotaControllerTest {
public void testIsWithinQuotaLocked_Charging() {
setCharging();
synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
}
}
@@ -1913,8 +1799,7 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -1931,7 +1816,7 @@ public class QuotaControllerTest {
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.spam", WORKING_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test.spam", WORKING_INDEX));
}
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
@@ -1941,7 +1826,7 @@ public class QuotaControllerTest {
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.frequent", FREQUENT_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test.frequent", FREQUENT_INDEX));
}
}
@@ -1957,8 +1842,7 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -1974,8 +1858,7 @@ public class QuotaControllerTest {
false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount);
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -2128,66 +2011,22 @@ public class QuotaControllerTest {
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
i < 2,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
i < 3,
mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test", FREQUENT_INDEX));
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
i < 4,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
i < 5,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
}
}
}
@Test
- public void testIsWithinQuotaLocked_Priority() {
- setDischarging();
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- synchronized (mQuotaController.mLock) {
- mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_LOW));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_MIN));
-
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_LOW));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_MIN));
-
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_LOW));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_MIN));
- }
- }
-
- @Test
public void testIsWithinEJQuotaLocked_NeverApp() {
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
setStandbyBucket(NEVER_INDEX, js);
@@ -2737,8 +2576,7 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2790,128 +2628,6 @@ public class QuotaControllerTest {
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
}
- @Test
- public void testMaybeScheduleStartAlarmLocked_Priority() {
- // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
- // because it schedules an alarm too. Prevent it from doing so.
- spyOn(mQuotaController);
- doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
-
- setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 5);
-
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
-
- InOrder inOrder = inOrder(mAlarmManager);
-
- JobStatus jobDef = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(1, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_DEFAULT)
- .build());
- JobStatus jobLow = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(2, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_LOW)
- .build());
- JobStatus jobMin = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(3, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_MIN)
- .build());
-
- setStandbyBucket(RARE_INDEX, jobDef, jobLow, jobMin);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Min job requires 5 mins of surplus.
- long expectedAlarmTime = now + 23 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Low job requires 2.5 mins of surplus.
- expectedAlarmTime = now + 17 * HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Default+ jobs require IN_QUOTA_BUFFER_MS.
- expectedAlarmTime = now + mQcConstants.IN_QUOTA_BUFFER_MS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
-
- setStandbyBucket(FREQUENT_INDEX, jobDef, jobLow, jobMin);
-
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Min job requires 5 mins of surplus.
- expectedAlarmTime = now + 7 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Low job requires 2.5 mins of surplus.
- expectedAlarmTime = now + HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Default+ jobs already have enough quota.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
-
- setStandbyBucket(WORKING_INDEX, jobDef, jobLow, jobMin);
-
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Min job requires 5 mins of surplus.
- expectedAlarmTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Low job has enough surplus.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Default+ jobs already have enough quota.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
- }
- }
-
/** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@Test
public void testMaybeScheduleStartAlarmLocked_BucketChange() {
@@ -3212,8 +2928,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
11 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
@@ -3269,8 +2983,6 @@ public class QuotaControllerTest {
assertEquals(11 * MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
- assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(99 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
@@ -3327,8 +3039,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
-MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
@@ -3379,8 +3089,6 @@ public class QuotaControllerTest {
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getInQuotaBufferMs());
- assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
@@ -3451,8 +3159,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
@@ -3492,8 +3198,6 @@ public class QuotaControllerTest {
assertEquals(24 * HOUR_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
- assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 6510cd19e5fb..cb9f003b6e3e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -70,7 +70,7 @@ import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
-import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
+import com.android.server.sdksandbox.SdkSandboxManagerLocal
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
import com.android.server.testutils.nullable
@@ -577,7 +577,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
1L, systemPartitions[0].privAppFolder,
withPackage = { pkg: PackageImpl ->
val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
- mockQueryServices(SupplementalProcessManagerLocal.SERVICE_INTERFACE,
+ mockQueryServices(SdkSandboxManagerLocal.SERVICE_INTERFACE,
createBasicServiceInfo(
pkg, applicationInfo, "SupplementalProcessService"))
pkg
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 53cab9ed80cf..a6194df18217 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -98,6 +98,8 @@
<uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
+ <uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" />
+ <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" />
<uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 953b5368c86f..1f016fb6f017 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -154,6 +154,7 @@ public class AccessibilityManagerServiceTest {
mMockWindowMagnificationMgr);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
+ when(mMockMagnificationController.supportWindowMagnification()).thenReturn(true);
when(mMockWindowManagerService.getAccessibilityController()).thenReturn(
mMockA11yController);
when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 3ce2ed84d3e8..f3a0b7fa1ea7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1158,7 +1158,7 @@ public class FullScreenMagnificationControllerTest {
MagnificationCallbacks callbacks = getMagnificationCallbacks(DISPLAY_0);
callbacks.onImeWindowVisibilityChanged(true);
mMessageCapturingHandler.sendAllMessages();
- verify(mRequestObserver).onImeWindowVisibilityChanged(eq(true));
+ verify(mRequestObserver).onImeWindowVisibilityChanged(eq(DISPLAY_0), eq(true));
}
private void setScaleToMagnifying() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index ec59090240f3..cc6d7611b4a1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -41,6 +41,7 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.MagnificationConfig;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -100,6 +101,8 @@ public class MagnificationControllerTest {
@Mock
private Context mContext;
@Mock
+ PackageManager mPackageManager;
+ @Mock
private FullScreenMagnificationController mScreenMagnificationController;
private MagnificationScaleProvider mScaleProvider;
@Captor
@@ -136,6 +139,7 @@ public class MagnificationControllerTest {
mMockResolver = new MockContentResolver();
mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
@@ -748,7 +752,7 @@ public class MagnificationControllerTest {
MagnificationController spyController = spy(mMagnificationController);
spyController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
- spyController.onImeWindowVisibilityChanged(true);
+ spyController.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
verify(spyController).logMagnificationModeWithIme(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
@@ -759,7 +763,7 @@ public class MagnificationControllerTest {
MagnificationController spyController = spy(mMagnificationController);
spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- spyController.onImeWindowVisibilityChanged(true);
+ spyController.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
verify(spyController).logMagnificationModeWithIme(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
@@ -768,7 +772,7 @@ public class MagnificationControllerTest {
@Test
public void imeWindowStateShown_noMagnifying_noLogAnyMode() {
MagnificationController spyController = spy(mMagnificationController);
- spyController.onImeWindowVisibilityChanged(true);
+ spyController.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
verify(spyController, never()).logMagnificationModeWithIme(anyInt());
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index 3822dc362b6b..4b77764c41e5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -94,6 +94,14 @@ public class WindowMagnificationConnectionWrapperTest {
}
@Test
+ public void moveWindowMagnifierToPosition() throws RemoteException {
+ mConnectionWrapper.moveWindowMagnifierToPosition(TEST_DISPLAY, 100, 150,
+ mAnimationCallback);
+ verify(mConnection).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(100f), eq(150f), any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
public void showMagnificationButton() throws RemoteException {
mConnectionWrapper.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 0742c09492f2..978000aa89d3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -21,6 +21,8 @@ import static com.android.server.accessibility.magnification.MockWindowMagnifica
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
@@ -54,6 +56,8 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityTraceManager;
@@ -99,12 +103,7 @@ public class WindowMagnificationManagerTest {
mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
when(mContext.getContentResolver()).thenReturn(mResolver);
- doAnswer((InvocationOnMock invocation) -> {
- final boolean connect = (Boolean) invocation.getArguments()[0];
- mWindowMagnificationManager.setConnection(
- connect ? mMockConnection.getConnection() : null);
- return null;
- }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+ stubSetConnection(false);
mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
Settings.Secure.putFloatForUser(mResolver,
@@ -112,6 +111,25 @@ public class WindowMagnificationManagerTest {
CURRENT_USER_ID);
}
+ private void stubSetConnection(boolean needDelay) {
+ doAnswer((InvocationOnMock invocation) -> {
+ final boolean connect = (Boolean) invocation.getArguments()[0];
+ // Simulates setConnection() called by another process.
+ if (needDelay) {
+ final Context context = ApplicationProvider.getApplicationContext();
+ context.getMainThreadHandler().postDelayed(
+ () -> {
+ mWindowMagnificationManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }, 10);
+ } else {
+ mWindowMagnificationManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }
+ return true;
+ }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+ }
+
@Test
public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
@@ -275,32 +293,33 @@ public class WindowMagnificationManagerTest {
}
@Test
- public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnification()
+ public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
- verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
- eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- eq(0f), eq(0f), notNull());
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnification()
+ public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
throws RemoteException {
final float distanceX = 10f;
final float distanceY = 10f;
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
@@ -310,16 +329,16 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
- verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
- eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- eq(0f), eq(0f), notNull());
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnification()
+ public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
@@ -328,15 +347,32 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
- verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
- eq(3f), eq(500f), eq(500f), eq(0f), eq(0f), notNull());
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+ @Test
+ public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnification()
+ public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
@@ -345,38 +381,56 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- eq(0f), eq(0f), notNull());
+ any(IRemoteMagnificationAnimationCallback.class));
}
@Test
- public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnification()
+ public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
throws RemoteException {
- final PointF initialPoint = new PointF(50f, 50f);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f,
- initialPoint.x, initialPoint.y);
- mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY),
- eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- eq(0f), eq(0f), notNull());
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
}
@Test
- public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification()
+ public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region beforeRegion = new Region();
mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
final Rect requestedRect = beforeRegion.getBounds();
@@ -392,6 +446,48 @@ public class WindowMagnificationManagerTest {
}
@Test
+ public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ // Enabling a window magnifier again will turn on the tracking typing focus functionality.
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
@@ -464,7 +560,7 @@ public class WindowMagnificationManagerTest {
public void
requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ assertTrue(mWindowMagnificationManager.requestConnection(true));
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
assertTrue(mWindowMagnificationManager.requestConnection(false));
@@ -499,7 +595,7 @@ public class WindowMagnificationManagerTest {
@Test
public void requestConnectionToNull_expectedGetterResults() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.requestConnection(true);
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
mWindowMagnificationManager.requestConnection(false);
@@ -513,6 +609,20 @@ public class WindowMagnificationManagerTest {
}
@Test
+ public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
+ throws RemoteException {
+ stubSetConnection(true);
+ mWindowMagnificationManager.requestConnection(true);
+
+ assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1));
+
+ // Invoke enableWindowMagnification if the connection is connected.
+ verify(mMockConnection.getConnection()).enableWindowMagnification(
+ eq(TEST_DISPLAY), eq(3f),
+ eq(1f), eq(1f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index b255a35c512e..25cf8a86baad 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -219,7 +219,7 @@ public class AuthSessionTest {
public void testMultiAuth_singleSensor_fingerprintSensorStartsAfterDialogAnimationCompletes()
throws Exception {
setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
- testMultiAuth_fingerprintSensorStartsAfter(false /* fingerprintStartsAfterDelay */);
+ testMultiAuth_fingerprintSensorStartsAfterUINotifies();
}
@Test
@@ -227,10 +227,10 @@ public class AuthSessionTest {
throws Exception {
setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
- testMultiAuth_fingerprintSensorStartsAfter(true /* fingerprintStartsAfterDelay */);
+ testMultiAuth_fingerprintSensorStartsAfterUINotifies();
}
- public void testMultiAuth_fingerprintSensorStartsAfter(boolean fingerprintStartsAfterDelay)
+ public void testMultiAuth_fingerprintSensorStartsAfterUINotifies()
throws Exception {
final long operationId = 123;
final int userId = 10;
@@ -274,12 +274,6 @@ public class AuthSessionTest {
// Notify AuthSession that the UI is shown. Then, fingerprint sensor should be started.
session.onDialogAnimatedIn();
- if (fingerprintStartsAfterDelay) {
- assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
- assertEquals(BiometricSensor.STATE_COOKIE_RETURNED,
- session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
- session.onStartFingerprint();
- }
assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
assertEquals(BiometricSensor.STATE_AUTHENTICATING,
session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index b94b6908f030..2ad5eaeb9aaf 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
@@ -85,14 +86,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Random;
-import java.util.concurrent.atomic.AtomicLong;
@Presubmit
@SmallTest
public class BiometricServiceTest {
- private static final String TAG = "BiometricServiceTest";
-
private static final String TEST_PACKAGE_NAME = "test_package";
private static final long TEST_REQUEST_ID = 44;
@@ -153,7 +151,7 @@ public class BiometricServiceTest {
.thenReturn(mock(BiometricStrengthController.class));
when(mInjector.getTrustManager()).thenReturn(mTrustManager);
when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager);
- when(mInjector.getRequestGenerator()).thenReturn(new AtomicLong(TEST_REQUEST_ID - 1));
+ when(mInjector.getRequestGenerator()).thenReturn(() -> TEST_REQUEST_ID);
when(mResources.getString(R.string.biometric_error_hw_unavailable))
.thenReturn(ERROR_HW_UNAVAILABLE);
@@ -178,22 +176,22 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */, null /* authenticators */);
waitForIdle();
- verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession),
+ verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mAuthSession),
anyInt());
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
waitForIdle();
- assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mAuthSession.getState());
- mBiometricService.mCurrentAuthSession.binderDied();
+ mBiometricService.mAuthSession.binderDied();
waitForIdle();
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
}
@@ -205,31 +203,31 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */, null /* authenticators */);
waitForIdle();
- verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession),
+ verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mAuthSession),
anyInt());
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
- mBiometricService.mCurrentAuthSession.binderDied();
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
+ mBiometricService.mAuthSession.binderDied();
waitForIdle();
- assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertNotNull(mBiometricService.mAuthSession);
verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
assertEquals(STATE_CLIENT_DIED_CANCELLING,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
- verify(mBiometricService.mCurrentAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl)
+ verify(mBiometricService.mAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl)
.cancelAuthenticationFromService(any(), any(), anyLong());
// Simulate ERROR_CANCELED received from HAL
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
0 /* vendorCode */);
waitForIdle();
verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
@@ -265,12 +263,12 @@ public class BiometricServiceTest {
Authenticators.DEVICE_CREDENTIAL);
waitForIdle();
- assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertNotNull(mBiometricService.mAuthSession);
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
// StatusBar showBiometricDialog invoked
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[0]) /* sensorIds */,
eq(true) /* credentialAllowed */,
@@ -304,21 +302,21 @@ public class BiometricServiceTest {
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(0 /* id */,
- BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
mFingerprintAuthenticator);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS),
eq(0 /* vendorCode */));
}
@Test
public void testAuthenticate_notStrongEnough_returnsHardwareNotPresent() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
Authenticators.BIOMETRIC_STRONG);
@@ -335,7 +333,7 @@ public class BiometricServiceTest {
// is able to proceed.
final int[] modalities = new int[] {
- BiometricAuthenticator.TYPE_FINGERPRINT,
+ TYPE_FINGERPRINT,
BiometricAuthenticator.TYPE_FACE,
};
@@ -356,7 +354,7 @@ public class BiometricServiceTest {
// StatusBar showBiometricDialog invoked with face, which was set up to be STRONG
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[] {SENSOR_ID_FACE}),
eq(false) /* credentialAllowed */,
@@ -377,14 +375,14 @@ public class BiometricServiceTest {
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
mBiometricService.mImpl.registerAuthenticator(0 /* id */,
- BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
mFingerprintAuthenticator);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
eq(0 /* vendorCode */));
}
@@ -415,13 +413,13 @@ public class BiometricServiceTest {
waitForIdle();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
final byte[] HAT = generateRandomHAT();
- mBiometricService.mBiometricSensorReceiver.onAuthenticationSucceeded(
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationSucceeded(
SENSOR_ID_FACE,
HAT);
waitForIdle();
// Confirmation is required
assertEquals(STATE_AUTH_PENDING_CONFIRM,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
// Enrolled, not disabled in settings, user doesn't require confirmation in settings
resetReceivers();
@@ -431,25 +429,25 @@ public class BiometricServiceTest {
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
null /* authenticators */);
waitForIdle();
- mBiometricService.mBiometricSensorReceiver.onAuthenticationSucceeded(
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationSucceeded(
SENSOR_ID_FACE,
HAT);
waitForIdle();
// Confirmation not required, waiting for dialog to dismiss
assertEquals(STATE_AUTHENTICATED_PENDING_SYSUI,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
}
@Test
public void testAuthenticate_happyPathWithoutConfirmation_strongBiometric() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
testAuthenticate_happyPathWithoutConfirmation(true /* isStrongBiometric */);
}
@Test
public void testAuthenticate_happyPathWithoutConfirmation_weakBiometric() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
testAuthenticate_happyPathWithoutConfirmation(false /* isStrongBiometric */);
}
@@ -461,7 +459,7 @@ public class BiometricServiceTest {
waitForIdle();
// Creates a pending auth session with the correct initial states
- assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mAuthSession.getState());
// Invokes <Modality>Service#prepareForAuthentication
ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -477,19 +475,19 @@ public class BiometricServiceTest {
cookieCaptor.capture() /* cookie */,
anyBoolean() /* allowBackgroundAuthentication */);
- // onReadyForAuthentication, mCurrentAuthSession state OK
- mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue());
+ // onReadyForAuthentication, mAuthSession state OK
+ mBiometricService.mImpl.onReadyForAuthentication(TEST_REQUEST_ID, cookieCaptor.getValue());
waitForIdle();
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
// startPreparedClient invoked
- mBiometricService.mCurrentAuthSession.onDialogAnimatedIn();
+ mBiometricService.mAuthSession.onDialogAnimatedIn();
verify(mBiometricService.mSensors.get(0).impl)
.startPreparedClient(cookieCaptor.getValue());
// StatusBar showBiometricDialog invoked
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
any(),
eq(false) /* credentialAllowed */,
@@ -502,18 +500,18 @@ public class BiometricServiceTest {
// Hardware authenticated
final byte[] HAT = generateRandomHAT();
- mBiometricService.mBiometricSensorReceiver.onAuthenticationSucceeded(
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationSucceeded(
SENSOR_ID_FINGERPRINT,
HAT);
waitForIdle();
// Waiting for SystemUI to send dismissed callback
assertEquals(STATE_AUTHENTICATED_PENDING_SYSUI,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
// Notify SystemUI hardware authenticated
- verify(mBiometricService.mStatusBarService).onBiometricAuthenticated();
+ verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(TYPE_FINGERPRINT);
// SystemUI sends callback with dismissed reason
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
null /* credentialAttestation */);
waitForIdle();
@@ -527,7 +525,7 @@ public class BiometricServiceTest {
verify(mReceiver1).onAuthenticationSucceeded(
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
// Current session becomes null
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
@@ -542,11 +540,11 @@ public class BiometricServiceTest {
waitForIdle();
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
+ mBiometricService.mAuthSession.mPromptInfo.getAuthenticators());
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[0]) /* sensorIds */,
eq(true) /* credentialAllowed */,
@@ -578,16 +576,16 @@ public class BiometricServiceTest {
// Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
// sent to KeyStore yet
final byte[] HAT = generateRandomHAT();
- mBiometricService.mBiometricSensorReceiver.onAuthenticationSucceeded(
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationSucceeded(
SENSOR_ID_FACE,
HAT);
waitForIdle();
// Waiting for SystemUI to send confirmation callback
- assertEquals(STATE_AUTH_PENDING_CONFIRM, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PENDING_CONFIRM, mBiometricService.mAuthSession.getState());
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
// SystemUI sends confirm, HAT is sent to keystore and client is notified.
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
null /* credentialAttestation */);
waitForIdle();
@@ -624,33 +622,34 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onAuthenticationFailed(SENSOR_ID_FACE);
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationFailed(SENSOR_ID_FACE);
waitForIdle();
verify(mBiometricService.mStatusBarService).onBiometricError(
- eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
- assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mAuthSession.getState());
}
@Test
public void testRejectFingerprint_whenAuthenticating_notifiesAndKeepsAuthenticating()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onAuthenticationFailed(SENSOR_ID_FINGERPRINT);
+ mBiometricService.mAuthSession.mSensorReceiver
+ .onAuthenticationFailed(SENSOR_ID_FINGERPRINT);
waitForIdle();
verify(mBiometricService.mStatusBarService).onBiometricError(
- eq(BiometricAuthenticator.TYPE_NONE),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
}
@Test
@@ -678,14 +677,14 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
waitForIdle();
- assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_TIMEOUT),
@@ -694,15 +693,15 @@ public class BiometricServiceTest {
verify(mReceiver1, never()).onAuthenticationFailed();
// No auth session. Pressing try again will create one.
- assertEquals(STATE_AUTH_PAUSED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED, mBiometricService.mAuthSession.getState());
// Pressing "Try again" on SystemUI
- mBiometricService.mSysuiReceiver.onTryAgainPressed();
+ mBiometricService.mAuthSession.mSysuiReceiver.onTryAgainPressed();
waitForIdle();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// AuthSession is now resuming
- assertEquals(STATE_AUTH_PAUSED_RESUMING, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_PAUSED_RESUMING, mBiometricService.mAuthSession.getState());
// Test resuming when hardware becomes ready. SystemUI should not be requested to
// show another dialog since it's already showing.
@@ -728,14 +727,14 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
0 /* vendorCode */);
waitForIdle();
@@ -748,7 +747,7 @@ public class BiometricServiceTest {
// Dialog is hidden immediately
verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
// Auth session is over
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
@@ -757,61 +756,61 @@ public class BiometricServiceTest {
// For errors that show in SystemUI, BiometricService stays in STATE_ERROR_PENDING_SYSUI
// until SystemUI notifies us that the dialog is dismissed at which point the current
// session is done.
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
0 /* vendorCode */);
waitForIdle();
// Sends error to SystemUI and does not notify client yet
- assertEquals(STATE_ERROR_PENDING_SYSUI, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_ERROR_PENDING_SYSUI, mBiometricService.mAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
eq(0 /* vendorCode */));
verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// SystemUI animation completed, client is notified, auth session is over
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_ERROR, null /* credentialAttestation */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
eq(0 /* vendorCode */));
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
public void testErrorFromHal_whilePreparingAuthentication_credentialAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */,
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
waitForIdle();
- assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
- mBiometricService.mBiometricSensorReceiver.onError(
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mAuthSession.getState());
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForPendingSession(mBiometricService.mCurrentAuthSession),
+ getCookieForPendingSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
0 /* vendorCode */);
waitForIdle();
// We should be showing device credential now
- assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertNotNull(mBiometricService.mAuthSession);
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
+ mBiometricService.mAuthSession.mPromptInfo.getAuthenticators());
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[0]) /* sensorIds */,
eq(true) /* credentialAllowed */,
@@ -826,23 +825,23 @@ public class BiometricServiceTest {
@Test
public void testErrorFromHal_whilePreparingAuthentication_credentialNotAllowed()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
waitForIdle();
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForPendingSession(mBiometricService.mCurrentAuthSession),
+ getCookieForPendingSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
0 /* vendorCode */);
waitForIdle();
// Error is sent to client
- verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ verify(mReceiver1).onError(eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT),
eq(0) /* vendorCode */);
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
@@ -861,7 +860,7 @@ public class BiometricServiceTest {
private void testBiometricAuth_whenLockout(@LockoutTracker.LockoutMode int lockoutMode,
int biometricPromptError) throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
.thenReturn(lockoutMode);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
@@ -869,16 +868,15 @@ public class BiometricServiceTest {
waitForIdle();
// Modality and error are sent
- verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ verify(mReceiver1).onError(eq(TYPE_FINGERPRINT),
eq(biometricPromptError), eq(0) /* vendorCode */);
}
@Test
public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential()
throws Exception {
- when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
- .thenReturn(true);
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
.thenReturn(LockoutTracker.LOCKOUT_PERMANENT);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
@@ -887,13 +885,13 @@ public class BiometricServiceTest {
waitForIdle();
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
- assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertNotNull(mBiometricService.mAuthSession);
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
assertEquals(Authenticators.DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
+ mBiometricService.mAuthSession.mPromptInfo.getAuthenticators());
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[0]) /* sensorIds */,
eq(true) /* credentialAllowed */,
@@ -959,73 +957,73 @@ public class BiometricServiceTest {
@Test
public void testErrorFromHal_whileShowingDeviceCredential_doesntNotifySystemUI()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */,
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
- mBiometricService.mSysuiReceiver.onDeviceCredentialPressed();
+ mBiometricService.mAuthSession.mSysuiReceiver.onDeviceCredentialPressed();
waitForIdle();
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
0 /* vendorCode */);
waitForIdle();
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
}
@Test
public void testLockout_whileAuthenticating_credentialAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */,
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
0 /* vendorCode */);
waitForIdle();
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT),
eq(0 /* vendorCode */));
}
@Test
public void testLockout_whenAuthenticating_credentialNotAllowed() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
0 /* vendorCode */);
waitForIdle();
assertEquals(STATE_ERROR_PENDING_SYSUI,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
verify(mBiometricService.mStatusBarService).onBiometricError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
eq(0 /* vendorCode */));
}
@@ -1033,20 +1031,20 @@ public class BiometricServiceTest {
@Test
public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
eq(0 /* vendorCode */));
verify(mBiometricService.mSensors.get(0).impl).cancelAuthenticationFromService(
any(), any(), anyLong());
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
@@ -1055,12 +1053,12 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_NEGATIVE, null /* credentialAttestation */);
waitForIdle();
@@ -1069,18 +1067,17 @@ public class BiometricServiceTest {
}
@Test
- public void testDismissedReasonUserCancel_whilePaused_invokesHalCancel() throws
- Exception {
+ public void testDismissedReasonUserCancel_whilePaused_invokesHalCancel() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FACE,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
@@ -1094,10 +1091,10 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onAuthenticationSucceeded(
+ mBiometricService.mAuthSession.mSensorReceiver.onAuthenticationSucceeded(
SENSOR_ID_FACE,
new byte[69] /* HAT */);
- mBiometricService.mSysuiReceiver.onDialogDismissed(
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
@@ -1108,19 +1105,19 @@ public class BiometricServiceTest {
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
eq(0 /* vendorCode */));
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
- assertNull(mBiometricService.mCurrentAuthSession);
+ assertNull(mBiometricService.mAuthSession);
}
@Test
public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
when(mContext.getResources().getString(anyInt())).thenReturn("test string");
- final int modality = BiometricAuthenticator.TYPE_FINGERPRINT;
+ final int modality = TYPE_FINGERPRINT;
setupAuthForOnly(modality, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mBiometricSensorReceiver.onAcquired(
+ mBiometricService.mAuthSession.mSensorReceiver.onAcquired(
SENSOR_ID_FINGERPRINT,
FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
0 /* vendorCode */);
@@ -1130,29 +1127,29 @@ public class BiometricServiceTest {
// string is retrieved for now, but it's also very unlikely to break anyway.
verify(mBiometricService.mStatusBarService)
.onBiometricHelp(eq(modality), anyString());
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
}
@Test
public void testCancel_whenAuthenticating() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken,
+ mBiometricService.mImpl.cancelAuthentication(mBiometricService.mAuthSession.mToken,
TEST_PACKAGE_NAME, TEST_REQUEST_ID);
waitForIdle();
// Pretend that the HAL has responded to cancel with ERROR_CANCELED
- mBiometricService.mBiometricSensorReceiver.onError(
+ mBiometricService.mAuthSession.mSensorReceiver.onError(
SENSOR_ID_FINGERPRINT,
- getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ getCookieForCurrentSession(mBiometricService.mAuthSession),
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
0 /* vendorCode */);
waitForIdle();
// Hides system dialog and invokes the onError callback
- verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ verify(mReceiver1).onError(eq(TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
eq(0 /* vendorCode */));
verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
@@ -1161,7 +1158,7 @@ public class BiometricServiceTest {
@Test
public void testCanAuthenticate_whenDeviceHasRequestedBiometricStrength() throws Exception {
// When only biometric is requested, and sensor is strong enough
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
@@ -1170,7 +1167,7 @@ public class BiometricServiceTest {
@Test
public void testCanAuthenticate_whenDeviceDoesNotHaveRequestedBiometricStrength()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
// When only biometric is requested, and sensor is not strong enough
when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
@@ -1208,9 +1205,8 @@ public class BiometricServiceTest {
@Test
public void testCanAuthenticate_whenNoBiometricsEnrolled() throws Exception {
// With credential set up, test the following.
- when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
- .thenReturn(true);
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
false /* enrolled */);
// When only biometric is requested
@@ -1277,7 +1273,7 @@ public class BiometricServiceTest {
private void testCanAuthenticate_whenLockedOut(@LockoutTracker.LockoutMode int lockoutMode)
throws Exception {
// When only biometric is requested, and sensor is strong enough
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
.thenReturn(lockoutMode);
@@ -1311,7 +1307,7 @@ public class BiometricServiceTest {
for (int i = 0; i < testCases.length; i++) {
final BiometricSensor sensor =
new BiometricSensor(mContext, 0 /* id */,
- BiometricAuthenticator.TYPE_FINGERPRINT,
+ TYPE_FINGERPRINT,
testCases[i][0],
mock(IBiometricAuthenticator.class)) {
@Override
@@ -1341,7 +1337,7 @@ public class BiometricServiceTest {
.thenReturn(true);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
mBiometricService.mImpl.registerAuthenticator(0 /* testId */,
- BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
mFingerprintAuthenticator);
verify(mBiometricService.mBiometricStrengthController).updateStrengths();
@@ -1360,7 +1356,7 @@ public class BiometricServiceTest {
.thenReturn(true);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
mBiometricService.mImpl.registerAuthenticator(testId /* id */,
- BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
mFingerprintAuthenticator);
// Downgrade the authenticator
@@ -1378,7 +1374,7 @@ public class BiometricServiceTest {
false /* requireConfirmation */, authenticators);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(TYPE_FINGERPRINT),
eq(BiometricPrompt.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED),
eq(0) /* vendorCode */);
@@ -1392,7 +1388,7 @@ public class BiometricServiceTest {
authenticators);
waitForIdle();
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[] {testId}),
eq(false) /* credentialAllowed */,
@@ -1414,9 +1410,9 @@ public class BiometricServiceTest {
false /* requireConfirmation */,
authenticators);
waitForIdle();
- assertTrue(Utils.isCredentialRequested(mBiometricService.mCurrentAuthSession.mPromptInfo));
+ assertTrue(Utils.isCredentialRequested(mBiometricService.mAuthSession.mPromptInfo));
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[0]) /* sensorIds */,
eq(true) /* credentialAllowed */,
@@ -1442,7 +1438,7 @@ public class BiometricServiceTest {
false /* requireConfirmation */, authenticators);
waitForIdle();
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
- eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+ eq(mBiometricService.mAuthSession.mPromptInfo),
any(IBiometricSysuiReceiver.class),
AdditionalMatchers.aryEq(new int[] {testId}) /* sensorIds */,
eq(false) /* credentialAllowed */,
@@ -1495,29 +1491,29 @@ public class BiometricServiceTest {
@Test
public void testWorkAuthentication_fingerprintWorksIfNotDisabledByDevicePolicyManager()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mDevicePolicyManager
.getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
.thenReturn(~DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mAuthSession.getState());
startPendingAuthSession(mBiometricService);
waitForIdle();
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
}
@Test
public void testAuthentication_normalAppIgnoresDevicePolicy() throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mDevicePolicyManager
.getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
}
@Test
@@ -1530,18 +1526,17 @@ public class BiometricServiceTest {
invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver1,
Authenticators.BIOMETRIC_STRONG);
waitForIdle();
- assertEquals(STATE_AUTH_CALLED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_CALLED, mBiometricService.mAuthSession.getState());
startPendingAuthSession(mBiometricService);
waitForIdle();
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
}
@Test
public void testWorkAuthentication_fingerprintFailsIfDisabledByDevicePolicyManager()
throws Exception {
- setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
- when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
- .thenReturn(true);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true);
when(mDevicePolicyManager
.getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
@@ -1555,9 +1550,9 @@ public class BiometricServiceTest {
invokeAuthenticateForWorkApp(mBiometricService.mImpl, mReceiver2,
Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
waitForIdle();
- assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertNotNull(mBiometricService.mAuthSession);
assertEquals(STATE_SHOWING_DEVICE_CREDENTIAL,
- mBiometricService.mCurrentAuthSession.getState());
+ mBiometricService.mAuthSession.getState());
verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt());
}
@@ -1580,7 +1575,7 @@ public class BiometricServiceTest {
when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
- if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) {
+ if ((modality & TYPE_FINGERPRINT) != 0) {
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
.thenReturn(enrolled);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
@@ -1614,7 +1609,7 @@ public class BiometricServiceTest {
final int modality = modalities[i];
final int strength = strengths[i];
- if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) {
+ if ((modality & TYPE_FINGERPRINT) != 0) {
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
.thenReturn(true);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
@@ -1654,8 +1649,9 @@ public class BiometricServiceTest {
startPendingAuthSession(mBiometricService);
waitForIdle();
- assertNotNull(mBiometricService.mCurrentAuthSession);
- assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+ assertNotNull(mBiometricService.mAuthSession);
+ assertEquals(TEST_REQUEST_ID, mBiometricService.mAuthSession.getRequestId());
+ assertEquals(STATE_AUTH_STARTED, mBiometricService.mAuthSession.getState());
return requestId;
}
@@ -1663,14 +1659,14 @@ public class BiometricServiceTest {
private static void startPendingAuthSession(BiometricService service) throws Exception {
// Get the cookie so we can pretend the hardware is ready to authenticate
// Currently we only support single modality per auth
- final PreAuthInfo preAuthInfo = service.mCurrentAuthSession.mPreAuthInfo;
+ final PreAuthInfo preAuthInfo = service.mAuthSession.mPreAuthInfo;
assertEquals(preAuthInfo.eligibleSensors.size(), 1);
assertEquals(preAuthInfo.numSensorsWaitingForCookie(), 1);
final int cookie = preAuthInfo.eligibleSensors.get(0).getCookie();
assertNotEquals(cookie, 0);
- service.mImpl.onReadyForAuthentication(cookie);
+ service.mImpl.onReadyForAuthentication(TEST_REQUEST_ID, cookie);
}
private static long invokeAuthenticate(IBiometricService.Stub service,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 64be56906d4b..eab96c09a00a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -40,12 +40,14 @@ import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@Presubmit
@RunWith(AndroidTestingRunner.class)
@@ -62,6 +64,9 @@ public class BiometricSchedulerOperationTest {
}
}
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
@Mock
private InterruptableMonitor<FakeHal> mClientMonitor;
@Mock
@@ -76,7 +81,6 @@ public class BiometricSchedulerOperationTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mHandler = new Handler(TestableLooper.get(this).getLooper());
mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
}
@@ -311,10 +315,12 @@ public class BiometricSchedulerOperationTest {
private void cancelWatchdog(boolean start) {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- mOperation.start(mock(ClientMonitorCallback.class));
+ final ClientMonitorCallback opStartCallback = mock(ClientMonitorCallback.class);
+ mOperation.start(opStartCallback);
if (start) {
verify(mClientMonitor).start(mStartCallback.capture());
mStartCallback.getValue().onClientStarted(mClientMonitor);
+ verify(opStartCallback).onClientStarted(eq(mClientMonitor));
}
mOperation.cancel(mHandler, mock(ClientMonitorCallback.class));
@@ -325,6 +331,7 @@ public class BiometricSchedulerOperationTest {
assertThat(mOperation.isFinished()).isTrue();
assertThat(mOperation.isCanceling()).isFalse();
+ verify(opStartCallback).onClientFinished(eq(mClientMonitor), eq(false));
verify(mClientMonitor).destroy();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
index bfb0be760f85..f40b31a0bc0d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
@@ -29,12 +29,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
-import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
-import android.os.Handler;
-import android.os.Looper;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -43,9 +39,11 @@ import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.LinkedList;
@@ -53,39 +51,38 @@ import java.util.LinkedList;
@SmallTest
public class CoexCoordinatorTest {
- private static final String TAG = "CoexCoordinatorTest";
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
- private CoexCoordinator mCoexCoordinator;
- private Handler mHandler;
-
- @Mock
- private Context mContext;
@Mock
private CoexCoordinator.Callback mCallback;
@Mock
private CoexCoordinator.ErrorCallback mErrorCallback;
+ @Mock
+ private AuthenticationClient mFaceClient;
+ @Mock
+ private AuthenticationClient mFingerprintClient;
+ @Mock(extraInterfaces = {Udfps.class})
+ private AuthenticationClient mUdfpsClient;
+
+ private CoexCoordinator mCoexCoordinator;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mHandler = new Handler(Looper.getMainLooper());
-
mCoexCoordinator = CoexCoordinator.getInstance();
mCoexCoordinator.setAdvancedLogicEnabled(true);
mCoexCoordinator.setFaceHapticDisabledWhenNonBypass(true);
+ mCoexCoordinator.reset();
}
@Test
public void testBiometricPrompt_authSuccess() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isBiometricPrompt()).thenReturn(true);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, client, mCallback);
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mFaceClient, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
@@ -93,15 +90,12 @@ public class CoexCoordinatorTest {
@Test
public void testBiometricPrompt_authReject_whenNotLockedOut() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isBiometricPrompt()).thenReturn(true);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- client, LockoutTracker.LOCKOUT_NONE, mCallback);
+ mFaceClient, LockoutTracker.LOCKOUT_NONE, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
@@ -109,30 +103,97 @@ public class CoexCoordinatorTest {
@Test
public void testBiometricPrompt_authReject_whenLockedOut() {
- mCoexCoordinator.reset();
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
- client, LockoutTracker.LOCKOUT_TIMED, mCallback);
+ mFaceClient, LockoutTracker.LOCKOUT_TIMED, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
verify(mCallback).handleLifecycleAfterAuth();
}
@Test
- public void testKeyguard_faceAuthOnly_success() {
- mCoexCoordinator.reset();
+ public void testBiometricPrompt_coex_success() {
+ testBiometricPrompt_coex_success(false /* twice */);
+ }
+
+ @Test
+ public void testBiometricPrompt_coex_successWithoutDouble() {
+ testBiometricPrompt_coex_success(true /* twice */);
+ }
+
+ private void testBiometricPrompt_coex_success(boolean twice) {
+ initFaceAndFingerprintForBiometricPrompt();
+ when(mFaceClient.wasAuthSuccessful()).thenReturn(true);
+ when(mUdfpsClient.wasAuthSuccessful()).thenReturn(twice, true);
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
+
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mFaceClient, mCallback);
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mUdfpsClient, mCallback);
+
+ if (twice) {
+ verify(mCallback, never()).sendHapticFeedback();
+ } else {
+ verify(mCallback).sendHapticFeedback();
+ }
+ }
+
+ @Test
+ public void testBiometricPrompt_coex_reject() {
+ initFaceAndFingerprintForBiometricPrompt();
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
+
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
+ mFaceClient, LockoutTracker.LOCKOUT_NONE, mCallback);
+
+ verify(mCallback, never()).sendHapticFeedback();
+
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
+ mUdfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback);
+
+ verify(mCallback).sendHapticFeedback();
+ }
+
+ @Test
+ public void testBiometricPrompt_coex_errorNoHaptics() {
+ initFaceAndFingerprintForBiometricPrompt();
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
+
+ mCoexCoordinator.onAuthenticationError(mFaceClient,
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
+ mCoexCoordinator.onAuthenticationError(mUdfpsClient,
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
+
+ verify(mErrorCallback, never()).sendHapticFeedback();
+ }
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isKeyguard()).thenReturn(true);
+ private void initFaceAndFingerprintForBiometricPrompt() {
+ when(mFaceClient.isKeyguard()).thenReturn(false);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
+ when(mFaceClient.wasAuthAttempted()).thenReturn(true);
+ when(mUdfpsClient.isKeyguard()).thenReturn(false);
+ when(mUdfpsClient.isBiometricPrompt()).thenReturn(true);
+ when(mUdfpsClient.wasAuthAttempted()).thenReturn(true);
+ }
+
+ @Test
+ public void testKeyguard_faceAuthOnly_success() {
+ when(mFaceClient.isKeyguard()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, client, mCallback);
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mFaceClient, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
@@ -140,21 +201,16 @@ public class CoexCoordinatorTest {
@Test
public void testKeyguard_faceAuth_udfpsNotTouching_faceSuccess() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
- mCallback);
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mFaceClient, mCallback);
// Haptics tested in #testKeyguard_bypass_haptics. Let's leave this commented out (instead
// of removed) to keep this context.
// verify(mCallback).sendHapticFeedback();
@@ -192,25 +248,19 @@ public class CoexCoordinatorTest {
private void testKeyguard_bypass_haptics(boolean bypassEnabled, boolean faceAccepted,
boolean shouldReceiveHaptics) {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
if (faceAccepted) {
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mFaceClient,
mCallback);
} else {
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
LockoutTracker.LOCKOUT_NONE, mCallback);
}
@@ -244,24 +294,18 @@ public class CoexCoordinatorTest {
private void testKeyguard_faceAuth_udfpsTouching_faceSuccess(boolean thenUdfpsAccepted,
long udfpsRejectedAfterMs) {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
+ when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
- when (udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
// For easier reading
final CoexCoordinator.Callback faceCallback = mCallback;
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mFaceClient,
faceCallback);
verify(faceCallback, never()).sendHapticFeedback();
verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
@@ -272,9 +316,9 @@ public class CoexCoordinatorTest {
// Reset the mock
CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
assertEquals(1, mCoexCoordinator.mSuccessfulAuths.size());
- assertEquals(faceClient, mCoexCoordinator.mSuccessfulAuths.get(0).mAuthenticationClient);
+ assertEquals(mFaceClient, mCoexCoordinator.mSuccessfulAuths.get(0).mAuthenticationClient);
if (thenUdfpsAccepted) {
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, udfpsClient,
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mUdfpsClient,
udfpsCallback);
verify(udfpsCallback).sendHapticFeedback();
verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
@@ -284,7 +328,7 @@ public class CoexCoordinatorTest {
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
} else {
- mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
+ mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, mUdfpsClient,
LockoutTracker.LOCKOUT_NONE, udfpsCallback);
if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
verify(udfpsCallback, never()).sendHapticFeedback();
@@ -310,56 +354,44 @@ public class CoexCoordinatorTest {
@Test
public void testKeyguard_udfpsAuthSuccess_whileFaceScanning() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
-
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, udfpsClient,
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, mUdfpsClient,
mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true));
- verify(faceClient).cancel();
+ verify(mFaceClient).cancel();
verify(mCallback).handleLifecycleAfterAuth();
}
@Test
public void testKeyguard_faceRejectedWhenUdfpsTouching_thenUdfpsRejected() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
LockoutTracker.LOCKOUT_NONE, mCallback);
verify(mCallback, never()).sendHapticFeedback();
verify(mCallback).handleLifecycleAfterAuth();
// BiometricScheduler removes the face authentication client after rejection
- mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+ mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
// Then UDFPS rejected
CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
- mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, udfpsClient,
+ mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, mUdfpsClient,
LockoutTracker.LOCKOUT_NONE, udfpsCallback);
verify(udfpsCallback).sendHapticFeedback();
verify(udfpsCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
@@ -368,26 +400,20 @@ public class CoexCoordinatorTest {
@Test
public void testKeyguard_udfpsRejected_thenFaceRejected_noKeyguardBypass() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(faceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
+ when(mUdfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(true);
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
-
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient,
- LockoutTracker.LOCKOUT_NONE, mCallback);
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
+ mUdfpsClient, LockoutTracker.LOCKOUT_NONE, mCallback);
// Auth was attempted
- when(udfpsClient.getState())
+ when(mUdfpsClient.getState())
.thenReturn(AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED);
verify(mCallback, never()).sendHapticFeedback();
verify(mCallback).handleLifecycleAfterAuth();
@@ -395,7 +421,7 @@ public class CoexCoordinatorTest {
// Then face rejected. Note that scheduler leaves UDFPS in the CoexCoordinator since
// unlike face, its lifecycle becomes "paused" instead of "finished".
CoexCoordinator.Callback faceCallback = mock(CoexCoordinator.Callback.class);
- mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, mFaceClient,
LockoutTracker.LOCKOUT_NONE, faceCallback);
verify(faceCallback).sendHapticFeedback();
verify(faceCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
@@ -404,20 +430,16 @@ public class CoexCoordinatorTest {
@Test
public void testKeyguard_capacitiveAccepted_whenFaceScanning() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
-
- AuthenticationClient<?> fpClient = mock(AuthenticationClient.class);
- when(fpClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(fpClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFingerprintClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFingerprintClient.isKeyguard()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, fpClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, mFingerprintClient);
- mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, fpClient, mCallback);
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */,
+ mFingerprintClient, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
@@ -425,21 +447,16 @@ public class CoexCoordinatorTest {
@Test
public void testKeyguard_capacitiveRejected_whenFaceScanning() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
-
- AuthenticationClient<?> fpClient = mock(AuthenticationClient.class);
- when(fpClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
- when(fpClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFingerprintClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(mFingerprintClient.isKeyguard()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, fpClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FP_OTHER, mFingerprintClient);
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, fpClient,
- LockoutTracker.LOCKOUT_NONE, mCallback);
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */,
+ mFingerprintClient, LockoutTracker.LOCKOUT_NONE, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
@@ -447,14 +464,11 @@ public class CoexCoordinatorTest {
@Test
public void testNonKeyguard_rejectAndNotLockedOut() {
- mCoexCoordinator.reset();
+ when(mFaceClient.isKeyguard()).thenReturn(false);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(false);
- when(faceClient.isBiometricPrompt()).thenReturn(true);
-
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
LockoutTracker.LOCKOUT_NONE, mCallback);
verify(mCallback).sendHapticFeedback();
@@ -464,14 +478,11 @@ public class CoexCoordinatorTest {
@Test
public void testNonKeyguard_rejectLockedOut() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(false);
- when(faceClient.isBiometricPrompt()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(false);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, mFaceClient,
LockoutTracker.LOCKOUT_TIMED, mCallback);
verify(mCallback).sendHapticFeedback();
@@ -496,16 +507,13 @@ public class CoexCoordinatorTest {
@Test
public void testBiometricPrompt_FaceError() {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isBiometricPrompt()).thenReturn(true);
- when(client.wasAuthAttempted()).thenReturn(true);
+ when(mFaceClient.isBiometricPrompt()).thenReturn(true);
+ when(mFaceClient.wasAuthAttempted()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- mErrorCallback);
+ mCoexCoordinator.onAuthenticationError(mFaceClient,
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
verify(mErrorCallback).sendHapticFeedback();
}
@@ -520,18 +528,15 @@ public class CoexCoordinatorTest {
}
private void testKeyguard_faceAuthOnly(boolean bypassEnabled) {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
- when(client.isKeyguard()).thenReturn(true);
- when(client.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
- when(client.wasAuthAttempted()).thenReturn(true);
- when(client.wasUserDetected()).thenReturn(true);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+ when(mFaceClient.wasAuthAttempted()).thenReturn(true);
+ when(mFaceClient.wasUserDetected()).thenReturn(true);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
- mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- mErrorCallback);
+ mCoexCoordinator.onAuthenticationError(mFaceClient,
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
verify(mErrorCallback).sendHapticFeedback();
}
@@ -546,23 +551,17 @@ public class CoexCoordinatorTest {
}
private void testKeyguard_coex_faceError(boolean bypassEnabled) {
- mCoexCoordinator.reset();
-
- AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
- when(faceClient.isKeyguard()).thenReturn(true);
- when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
- when(faceClient.wasAuthAttempted()).thenReturn(true);
- when(faceClient.wasUserDetected()).thenReturn(true);
-
- AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
- withSettings().extraInterfaces(Udfps.class));
- when(udfpsClient.isKeyguard()).thenReturn(true);
- when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+ when(mFaceClient.isKeyguard()).thenReturn(true);
+ when(mFaceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+ when(mFaceClient.wasAuthAttempted()).thenReturn(true);
+ when(mFaceClient.wasUserDetected()).thenReturn(true);
+ when(mUdfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) mUdfpsClient).isPointerDown()).thenReturn(false);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
- mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, mFaceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, mUdfpsClient);
- mCoexCoordinator.onAuthenticationError(faceClient,
+ mCoexCoordinator.onAuthenticationError(mFaceClient,
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
if (bypassEnabled) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
new file mode 100644
index 000000000000..76a5accc5fe9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+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.HashMap;
+import java.util.Map;
+
+@Presubmit
+@SmallTest
+public class FaceRemovalClientTest {
+
+ private static final int USER_ID = 12;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private ISession mHal;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Sensor.HalSessionCallback mHalSessionCallback;
+ @Mock
+ private BiometricUtils<Face> mUtils;
+ @Mock
+ private BiometricAuthenticator.Identifier mIdentifier;
+ private Map<Integer, Long> mAuthenticatorIds = new HashMap<Integer, Long>();
+
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
+ @Test
+ public void testFaceRemovalClient() throws RemoteException {
+ final int authenticatorId = 1;
+ int[] authenticatorIds = new int[]{authenticatorId};
+ final FaceRemovalClient client = createClient(1, authenticatorIds);
+ when(mIdentifier.getBiometricId()).thenReturn(authenticatorId);
+ client.start(mCallback);
+ verify(mHal).removeEnrollments(authenticatorIds);
+ client.onRemoved(mIdentifier, 0 /* remaining */);
+ verify(mClientMonitorCallbackConverter).onRemoved(
+ eq(mIdentifier) /* identifier */, eq(0) /* remaining */);
+ verify(mCallback).onClientFinished(client, true);
+ }
+
+ @Test
+ public void clientSendsErrorWhenHALFailsToRemoveEnrollment() throws RemoteException {
+ final FaceRemovalClient client = createClient(1, new int[0]);
+ client.start(mCallback);
+ client.onRemoved(null, 0 /* remaining */);
+ verify(mClientMonitorCallbackConverter).onError(eq(5) /* sensorId */, anyInt(),
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_REMOVE), eq(0) /* vendorCode*/);
+ verify(mCallback).onClientFinished(client, false);
+ }
+
+ private FaceRemovalClient createClient(int version, int[] biometricIds) throws RemoteException {
+ when(mHal.getInterfaceVersion()).thenReturn(version);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ return new FaceRemovalClient(mContext, () -> aidl, mToken,
+ mClientMonitorCallbackConverter, biometricIds, USER_ID,
+ "own-it", mUtils /* utils */, 5 /* sensorId */, mBiometricLogger, mBiometricContext,
+ mAuthenticatorIds);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 9aac81c34edb..7b921ababf36 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -58,6 +58,7 @@ import android.os.IBinder;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
@@ -136,6 +137,7 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt());
doNothing().when(mContext).enforceCallingOrSelfPermission(
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 3160272ef9b1..f0c907d49a46 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioSessionCallback;
import android.content.Context;
import android.content.ContextWrapper;
@@ -72,7 +73,8 @@ public class VirtualAudioControllerTest {
/* allowedUsers= */ new ArraySet<>(),
/* allowedActivities= */ new ArraySet<>(),
/* blockedActivities= */ new ArraySet<>(),
- /* activityListener= */null,
+ VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED,
+ /* activityListener= */ null,
/* activityBlockedCallback= */ null);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index 533fb2d8d214..4f6fc3dc1f93 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -309,7 +309,7 @@ public class OverlayPackagesProviderTest {
@Test
public void testGetNonRequiredApps_managedProfile_roleHolder_works() {
- when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ when(mInjector.getDevicePolicyManagementRoleHolderPackageName(any()))
.thenReturn(ROLE_HOLDER_PACKAGE_NAME);
setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
@@ -319,7 +319,7 @@ public class OverlayPackagesProviderTest {
@Test
public void testGetNonRequiredApps_managedDevice_roleHolder_works() {
- when(mInjector.getDeviceManagerRoleHolderPackageName(any()))
+ when(mInjector.getDevicePolicyManagementRoleHolderPackageName(any()))
.thenReturn(ROLE_HOLDER_PACKAGE_NAME);
setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index 0287510041be..bd35be4d0f3e 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -107,6 +107,7 @@ public class LocaleManagerBackupRestoreTest {
private PackageManager mMockPackageManager;
@Mock
private LocaleManagerService mMockLocaleManagerService;
+
BroadcastReceiver mUserMonitor;
PackageMonitor mPackageMonitor;
@@ -131,6 +132,7 @@ public class LocaleManagerBackupRestoreTest {
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockPackageManager = mock(PackageManager.class);
mMockLocaleManagerService = mock(LocaleManagerService.class);
+ SystemAppUpdateTracker systemAppUpdateTracker = mock(SystemAppUpdateTracker.class);
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
@@ -144,7 +146,8 @@ public class LocaleManagerBackupRestoreTest {
doNothing().when(mBackupHelper).notifyBackupManager();
mUserMonitor = mBackupHelper.getUserMonitor();
- mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
+ systemAppUpdateTracker);
setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
new file mode 100644
index 000000000000..5185e15a8557
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.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.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.Xml;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link SystemAppUpdateTracker}.
+ */
+public class SystemAppUpdateTrackerTest {
+ private static final String DEFAULT_PACKAGE_NAME_1 = "com.android.myapp1";
+ private static final String DEFAULT_PACKAGE_NAME_2 = "com.android.myapp2";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
+ private static final LocaleList DEFAULT_LOCALES =
+ LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String SYSTEM_APPS_XML_TAG = "system_apps";
+ private static final int DEFAULT_USER_ID = 0;
+
+ private AtomicFile mStoragefile;
+ private static final String DEFAULT_INSTALLER_PACKAGE_NAME = "com.android.myapp.installer";
+ private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
+ /* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
+ /* originatingPackageName = */ null,
+ /* installingPackageName = */ DEFAULT_INSTALLER_PACKAGE_NAME,
+ /* packageSource = */ PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED);
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ PackageManager mMockPackageManager;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock
+ private ActivityTaskManagerInternal mMockActivityTaskManager;
+ @Mock
+ private ActivityManagerInternal mMockActivityManager;
+ @Mock
+ private LocaleManagerBackupHelper mMockLocaleManagerBackupHelper;
+ @Mock
+ PackageMonitor mMockPackageMonitor;
+
+ private LocaleManagerService mLocaleManagerService;
+
+ // Object under test.
+ private SystemAppUpdateTracker mSystemAppUpdateTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
+ mMockActivityManager = mock(ActivityManagerInternal.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockPackageMonitor = mock(PackageMonitor.class);
+ mMockLocaleManagerBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
+ mLocaleManagerService = new LocaleManagerService(mMockContext,
+ mMockActivityTaskManager, mMockActivityManager,
+ mMockPackageManagerInternal, mMockLocaleManagerBackupHelper, mMockPackageMonitor);
+
+ doReturn(DEFAULT_USER_ID).when(mMockActivityManager)
+ .handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
+ anyString(), anyString());
+
+ mMockPackageManager = mock(PackageManager.class);
+ doReturn(DEFAULT_INSTALL_SOURCE_INFO).when(mMockPackageManager)
+ .getInstallSourceInfo(anyString());
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+
+ mStoragefile = new AtomicFile(new File(
+ Environment.getExternalStorageDirectory(), "systemUpdateUnitTests.xml"));
+
+ mSystemAppUpdateTracker = new SystemAppUpdateTracker(mMockContext,
+ mLocaleManagerService, mStoragefile);
+ }
+
+ @After
+ public void tearDown() {
+ mStoragefile.delete();
+ }
+
+ @Test
+ public void testInit_loadsCorrectly() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+
+ // Updates the app once so that it writes to the file.
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+ // Clear the in-memory data of updated apps
+ mSystemAppUpdateTracker.getUpdatedApps().clear();
+ // Invoke init to verify if it correctly populates in-memory set.
+ mSystemAppUpdateTracker.init();
+
+ assertEquals(Set.of(DEFAULT_PACKAGE_NAME_1), mSystemAppUpdateTracker.getUpdatedApps());
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_systemAppFirstUpdate_writesToFile() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0,
+ DEFAULT_LOCALES)).when(mMockActivityTaskManager)
+ .getApplicationConfig(anyString(), anyInt());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
+ Set<String> expectedAppList = Set.of(DEFAULT_PACKAGE_NAME_1);
+ assertEquals(expectedAppList, mSystemAppUpdateTracker.getUpdatedApps());
+ verifyStorageFileContents(expectedAppList);
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_systemAppSecondUpdate_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0,
+ DEFAULT_LOCALES)).when(mMockActivityTaskManager)
+ .getApplicationConfig(anyString(), anyInt());
+
+ // first update
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
+ Set<String> expectedAppList = Set.of(DEFAULT_PACKAGE_NAME_1);
+ assertEquals(expectedAppList, mSystemAppUpdateTracker.getUpdatedApps());
+ verifyStorageFileContents(expectedAppList);
+
+ // second update
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+ // getApplicationLocales should be invoked only once on the first update.
+ verify(mMockActivityTaskManager, times(1))
+ .getApplicationConfig(anyString(), anyInt());
+ // Broadcast should be sent only once on first update.
+ verify(mMockContext, times(1)).sendBroadcastAsUser(any(), any());
+ // Verify that the content remains the same.
+ verifyStorageFileContents(expectedAppList);
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_notSystemApp_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_2,
+ /* isUpdatedSystemApp = */false))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_2), any());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_2,
+ Binder.getCallingUid());
+
+ assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
+ // getApplicationLocales should be never be invoked if not a system app.
+ verifyZeroInteractions(mMockActivityTaskManager);
+ // Broadcast should be never sent if not a system app.
+ verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
+ // It shouldn't write to the file if not a system app.
+ assertTrue(!mStoragefile.getBaseFile().isFile());
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_noInstaller_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(null).when(mMockPackageManager).getInstallSourceInfo(anyString());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ // getApplicationLocales should be never be invoked if not installer is not present.
+ verifyZeroInteractions(mMockActivityTaskManager);
+ // Broadcast should be never sent if installer is not present.
+ verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
+ // It shouldn't write to file if no installer present.
+ assertTrue(!mStoragefile.getBaseFile().isFile());
+ }
+
+ private void verifyStorageFileContents(Set<String> expectedAppList)
+ throws IOException, XmlPullParserException {
+ assertTrue(mStoragefile.getBaseFile().isFile());
+ try (InputStream storageInputStream = mStoragefile.openRead()) {
+ assertEquals(expectedAppList, readFromXml(storageInputStream));
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ }
+
+ private Set<String> readFromXml(InputStream storageInputStream)
+ throws XmlPullParserException, IOException {
+ Set<String> outputList = new HashSet<>();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(storageInputStream, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, SYSTEM_APPS_XML_TAG);
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_NAME);
+ if (!TextUtils.isEmpty(packageName)) {
+ outputList.add(packageName);
+ }
+ }
+ }
+ return outputList;
+ }
+
+ /**
+ * Verifies the broadcast sent to the installer of the updated app.
+ */
+ private void assertBroadcastSentToInstaller(String packageName, LocaleList locales) {
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext).sendBroadcastAsUser(captor.capture(), any(UserHandle.class));
+ for (Intent intent : captor.getAllValues()) {
+ assertTrue(Intent.ACTION_APPLICATION_LOCALE_CHANGED.equals(intent.getAction()));
+ assertTrue(DEFAULT_INSTALLER_PACKAGE_NAME.equals(intent.getPackage()));
+ assertTrue(packageName.equals(intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)));
+ assertTrue(locales.equals(intent.getParcelableExtra(Intent.EXTRA_LOCALE_LIST)));
+ }
+ }
+
+ private ApplicationInfo createApplicationInfoForApp(String packageName,
+ boolean isUpdatedSystemApp) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ if (isUpdatedSystemApp) {
+ applicationInfo.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ }
+ return applicationInfo;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index eeaf781dd307..bfdffc0e6567 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -39,9 +39,11 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
@@ -137,13 +139,14 @@ public class SystemConfigTest {
new ArraySet<>(Arrays.asList("GUEST", "PROFILE")));
final File folder1 = createTempSubfolder("folder1");
- createTempFile(folder1, "permFile1.xml", contents1);
+ createTempFile(folder1, "permissionFile1.xml", contents1);
final File folder2 = createTempSubfolder("folder2");
- createTempFile(folder2, "permFile2.xml", contents2);
+ createTempFile(folder2, "permissionFile2.xml", contents2);
- // Also, make a third file, but with the name folder1/permFile2.xml, to prove no conflicts.
- createTempFile(folder1, "permFile2.xml", contents3);
+ // Also, make a third file, but with the name folder1/permissionFile2.xml, to prove no
+ // conflicts.
+ createTempFile(folder1, "permissionFile2.xml", contents3);
readPermissions(folder1, /* No permission needed anyway */ 0);
readPermissions(folder2, /* No permission needed anyway */ 0);
@@ -333,6 +336,91 @@ public class SystemConfigTest {
assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
+ @Test
+ public void readApexPrivAppPermissions_addAllPermissions()
+ throws Exception {
+ final String contents =
+ "<privapp-permissions package=\"com.android.apk_in_apex\">"
+ + "<permission name=\"android.permission.FOO\"/>"
+ + "<deny-permission name=\"android.permission.BAR\"/>"
+ + "</privapp-permissions>";
+ File apexDir = createTempSubfolder("apex");
+ File permissionFile = createTempFile(
+ createTempSubfolder("apex/com.android.my_module/etc/permissions"),
+ "permissions.xml", contents);
+ XmlPullParser parser = readXmlUntilStartTag(permissionFile);
+
+ mSysConfig.readApexPrivAppPermissions(parser, permissionFile, apexDir.toPath());
+
+ assertThat(mSysConfig.getApexPrivAppPermissions("com.android.my_module",
+ "com.android.apk_in_apex"))
+ .containsExactly("android.permission.FOO");
+ assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.my_module",
+ "com.android.apk_in_apex"))
+ .containsExactly("android.permission.BAR");
+ }
+
+ @Test
+ public void pruneVendorApexPrivappAllowlists_removeVendor()
+ throws Exception {
+ File apexDir = createTempSubfolder("apex");
+
+ // Read non-vendor apex permission allowlists
+ final String allowlistNonVendorContents =
+ "<privapp-permissions package=\"com.android.apk_in_non_vendor_apex\">"
+ + "<permission name=\"android.permission.FOO\"/>"
+ + "<deny-permission name=\"android.permission.BAR\"/>"
+ + "</privapp-permissions>";
+ File nonVendorPermDir =
+ createTempSubfolder("apex/com.android.non_vendor/etc/permissions");
+ File nonVendorPermissionFile =
+ createTempFile(nonVendorPermDir, "permissions.xml", allowlistNonVendorContents);
+ XmlPullParser nonVendorParser = readXmlUntilStartTag(nonVendorPermissionFile);
+ mSysConfig.readApexPrivAppPermissions(nonVendorParser, nonVendorPermissionFile,
+ apexDir.toPath());
+
+ // Read vendor apex permission allowlists
+ final String allowlistVendorContents =
+ "<privapp-permissions package=\"com.android.apk_in_vendor_apex\">"
+ + "<permission name=\"android.permission.BAZ\"/>"
+ + "<deny-permission name=\"android.permission.BAT\"/>"
+ + "</privapp-permissions>";
+ File vendorPermissionFile =
+ createTempFile(createTempSubfolder("apex/com.android.vendor/etc/permissions"),
+ "permissions.xml", allowlistNonVendorContents);
+ XmlPullParser vendorParser = readXmlUntilStartTag(vendorPermissionFile);
+ mSysConfig.readApexPrivAppPermissions(vendorParser, vendorPermissionFile,
+ apexDir.toPath());
+
+ // Read allowed vendor apex list
+ final String allowedVendorContents =
+ "<config>\n"
+ + " <allowed-vendor-apex package=\"com.android.vendor\" "
+ + "installerPackage=\"com.installer\" />\n"
+ + "</config>";
+ final File allowedVendorFolder = createTempSubfolder("folder");
+ createTempFile(allowedVendorFolder, "vendor-apex-allowlist.xml", allowedVendorContents);
+ readPermissions(allowedVendorFolder, /* Grant all permission flags */ ~0);
+
+ // Finally, prune non-vendor allowlists.
+ // There is no guarantee in which order the above reads will be done, however pruning
+ // will always happen last.
+ mSysConfig.pruneVendorApexPrivappAllowlists();
+
+ assertThat(mSysConfig.getApexPrivAppPermissions("com.android.non_vendor",
+ "com.android.apk_in_non_vendor_apex"))
+ .containsExactly("android.permission.FOO");
+ assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.non_vendor",
+ "com.android.apk_in_non_vendor_apex"))
+ .containsExactly("android.permission.BAR");
+ assertThat(mSysConfig.getApexPrivAppPermissions("com.android.vendor",
+ "com.android.apk_in_vendor_apex"))
+ .isNull();
+ assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.vendor",
+ "com.android.apk_in_vendor_apex"))
+ .isNull();
+ }
+
/**
* Tests that readPermissions works correctly for a library with on-bootclasspath-before
* and on-bootclasspath-since.
@@ -492,6 +580,25 @@ public class SystemConfigTest {
}
/**
+ * Create an {@link XmlPullParser} for {@param permissionFile} and begin parsing it until
+ * reaching the root tag.
+ */
+ private XmlPullParser readXmlUntilStartTag(File permissionFile)
+ throws IOException, XmlPullParserException {
+ FileReader permReader = new FileReader(permissionFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(permReader);
+ int type;
+ do {
+ type = parser.next();
+ } while (type != parser.START_TAG && type != parser.END_DOCUMENT);
+ if (type != parser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+ return parser;
+ }
+
+ /**
* Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
*
* @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
@@ -500,7 +607,7 @@ public class SystemConfigTest {
private File createTempSubfolder(String folderName)
throws IOException {
File folder = new File(mTemporaryFolder.getRoot(), folderName);
- folder.mkdir();
+ folder.mkdirs();
return folder;
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
index 5e9e16a0baf8..1a146f636f6e 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -18,7 +18,10 @@ package com.android.server.vibrator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.os.Handler;
import android.os.VibrationEffect;
@@ -33,8 +36,14 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
+import com.android.server.LocalServices;
+
import org.junit.Before;
+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.Arrays;
import java.util.stream.IntStream;
@@ -59,10 +68,19 @@ public class DeviceVibrationEffectAdapterTest {
new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
+ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+
private DeviceVibrationEffectAdapter mAdapter;
@Before
public void setUp() throws Exception {
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+
VibrationSettings vibrationSettings = new VibrationSettings(
InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper()));
mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 81677101c139..0301e94a6acc 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -31,8 +31,10 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.IExternalVibratorService;
import android.os.PowerManagerInternal;
@@ -76,6 +78,7 @@ public class VibrationScalerTest {
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
@Mock private VibrationConfig mVibrationConfigMock;
private TestLooper mTestLooper;
@@ -90,7 +93,11 @@ public class VibrationScalerTest {
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 5d4ffbb6aca2..3cda2a554e48 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -47,13 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
import android.media.AudioManager;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
+import android.os.Process;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -87,6 +90,7 @@ import org.mockito.junit.MockitoRule;
public class VibrationSettingsTest {
private static final int UID = 1;
+ private static final String SYSUI_PACKAGE_NAME = "sysui";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
.setBatterySaverEnabled(true).build();
@@ -104,17 +108,13 @@ public class VibrationSettingsTest {
USAGE_TOUCH,
};
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Rule
- public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Mock
- private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
- @Mock
- private PowerManagerInternal mPowerManagerInternalMock;
- @Mock
- private VibrationConfig mVibrationConfigMock;
+ @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
+ @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock private VibrationConfig mVibrationConfigMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
@@ -129,14 +129,17 @@ public class VibrationSettingsTest {
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-
doAnswer(invocation -> {
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
mAudioManager = mContextSpy.getSystemService(AudioManager.class);
@@ -285,7 +288,7 @@ public class VibrationSettingsTest {
}
@Test
- public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
+ public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneOnly() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
@@ -293,7 +296,7 @@ public class VibrationSettingsTest {
setRingerMode(AudioManager.RINGER_MODE_SILENT);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_RINGTONE || usage == USAGE_TOUCH) {
+ if (usage == USAGE_RINGTONE) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
} else {
assertVibrationNotIgnoredForUsage(usage);
@@ -472,6 +475,55 @@ public class VibrationSettingsTest {
}
@Test
+ public void shouldCancelVibrationOnScreenOff_withNonSystemPackageAndUid_returnsAlwaysTrue() {
+ for (int usage : ALL_USAGES) {
+ assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(UID, "some.app", usage));
+ }
+ }
+
+ @Test
+ public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() {
+ for (int usage : ALL_USAGES) {
+ if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
+ || usage == USAGE_PHYSICAL_EMULATION) {
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ /* uid= */ 0, "", usage));
+ } else {
+ assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ /* uid= */ 0, "", usage));
+ }
+ }
+ }
+
+ @Test
+ public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() {
+ for (int usage : ALL_USAGES) {
+ if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
+ || usage == USAGE_PHYSICAL_EMULATION) {
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ Process.SYSTEM_UID, "", usage));
+ } else {
+ assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ Process.SYSTEM_UID, "", usage));
+ }
+ }
+ }
+
+ @Test
+ public void shouldCancelVibrationOnScreenOff_withSysUi_returnsFalseForTouchAndHardware() {
+ for (int usage : ALL_USAGES) {
+ if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
+ || usage == USAGE_PHYSICAL_EMULATION) {
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, SYSUI_PACKAGE_NAME, usage));
+ } else {
+ assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, SYSUI_PACKAGE_NAME, usage));
+ }
+ }
+ }
+
+ @Test
public void getDefaultIntensity_returnsIntensityFromVibratorConfig() {
setDefaultIntensity(VIBRATION_INTENSITY_HIGH);
setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 3d24a814c7cd..0590d7df5ee3 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -35,7 +35,9 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
@@ -61,6 +63,8 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
+import com.android.server.LocalServices;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -94,17 +98,13 @@ public class VibrationThreadTest {
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
private static final int TEST_RAMP_STEP_DURATION = 5;
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Mock
- private VibrationThread.VibratorManagerHooks mManagerHooks;
- @Mock
- private VibratorController.OnVibrationCompleteListener mControllerCallbacks;
- @Mock
- private IBinder mVibrationToken;
- @Mock
- private VibrationConfig mVibrationConfigMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock private VibrationThread.VibratorManagerHooks mManagerHooks;
+ @Mock private VibratorController.OnVibrationCompleteListener mControllerCallbacks;
+ @Mock private IBinder mVibrationToken;
+ @Mock private VibrationConfig mVibrationConfigMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
private VibrationSettings mVibrationSettings;
@@ -123,6 +123,11 @@ public class VibrationThreadTest {
when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt()))
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
Context context = InstrumentationRegistry.getContext();
mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 721641a7a8c8..5458a5b84eea 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -174,7 +174,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
verify(mCallback, times(AUTOGROUP_AT_COUNT + 1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -203,7 +203,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -236,7 +236,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -263,7 +263,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationRemoved(notifications.get(0));
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -291,7 +291,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -315,7 +315,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -339,7 +339,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
verify(mCallback, times(0))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg, AUTOGROUP_KEY), 0);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 90dac47e353c..57bbe40c5b06 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,6 +57,7 @@ android_test {
"ub-uiautomator",
"hamcrest-library",
"platform-compat-test-rules",
+ "CtsSurfaceValidatorLib",
],
libs: [
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index c2298d0f3d20..2918365b94c8 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -43,6 +43,8 @@
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
<uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -78,6 +80,12 @@
android:turnScreenOn="true"
android:showWhenLocked="true" />
<activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
+ <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
+
+ <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
+ android:foregroundServiceType="mediaProjection"
+ android:enabled="true">
+ </service>
</application>
<instrumentation
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 b3d6b3e04d59..2ea0bdc4354e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -54,6 +55,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -3397,6 +3399,24 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.mVisibleRequested);
}
+ @Test
+ public void testShellTransitionTaskWindowingModeChange() {
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertTrue(activity.isVisible());
+ assertTrue(activity.isVisibleRequested());
+ assertEquals(WINDOWING_MODE_FULLSCREEN, activity.getWindowingMode());
+
+ registerTestTransitionPlayer();
+ task.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, task);
+ task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ // Collect activity in the transition if the Task windowing mode is going to change.
+ assertTrue(activity.inTransition());
+ }
+
private ICompatCameraControlCallback getCompatCameraControlCallback() {
return new ICompatCameraControlCallback.Stub() {
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 55d6df95e66f..0c8394e75fbc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -352,6 +352,6 @@ public class ActivityStartInterceptorTest {
spyOn(callback);
mInterceptor.onActivityLaunched(null, null);
- verify(callback, times(1)).onActivityLaunched(any(), any());
+ verify(callback, times(1)).onActivityLaunched(any(), any(), any());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 16c5bfec76ae..6fe2d337e4a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1085,7 +1085,7 @@ public class ActivityStarterTests extends WindowTestsBase {
starter.setActivityOptions(options.toBundle())
.setReason("testWindowingModeOptionsLaunchAdjacent")
.setOutActivity(outActivity).execute();
- assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
+ assertThat(outActivity[0].inMultiWindowMode()).isFalse();
// Move activity to split-screen-primary stack and make sure it has the focus.
TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
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 9f7130e45483..925f4f5a4130 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -75,7 +75,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
@@ -1117,7 +1116,7 @@ public class DisplayContentTests extends WindowTestsBase {
app.removeImmediately();
- assertNull(dc.getImeTarget(IME_TARGET_INPUT));
+ assertNull(dc.getImeInputTarget());
assertNull(dc.computeImeControlTarget());
}
@@ -1126,19 +1125,19 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
- dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
- assertEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(), dc.computeImeControlTarget());
+ dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
+ assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget());
}
@Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
- dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
+ dc.getImeInputTarget().getWindowState().setWindowingMode(
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
- dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
+ dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
- assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
+ assertNotEquals(dc.getImeInputTarget().getWindowState(),
dc.computeImeControlTarget());
}
@@ -1149,7 +1148,7 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
mDisplayContent.setImeInputTarget(mAppWindow);
mDisplayContent.setImeLayeringTarget(
- mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow());
+ mDisplayContent.getImeInputTarget().getWindowState());
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
@@ -1915,7 +1914,7 @@ public class DisplayContentTests extends WindowTestsBase {
final WindowState nextImeTargetApp = createWindow(null /* parent */,
TYPE_BASE_APPLICATION, "nextImeTargetApp");
spyOn(child1);
- doReturn(true).when(child1).inSplitScreenWindowingMode();
+ doReturn(false).when(mDisplayContent).shouldImeAttachedToApp();
mDisplayContent.setImeLayeringTarget(child1);
spyOn(nextImeTargetApp);
@@ -1927,7 +1926,7 @@ public class DisplayContentTests extends WindowTestsBase {
child1.removeImmediately();
verify(mDisplayContent).computeImeTarget(true);
- assertNull(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ assertNull(mDisplayContent.getImeInputTarget());
verify(child1, never()).needsRelativeLayeringToIme();
}
@@ -2419,10 +2418,10 @@ public class DisplayContentTests extends WindowTestsBase {
public void testKeepClearAreasMultipleWindows() {
final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
final Rect rect1 = new Rect(0, 0, 10, 10);
- w1.setKeepClearAreas(Arrays.asList(rect1));
+ w1.setKeepClearAreas(Arrays.asList(rect1), Collections.emptyList());
final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
final Rect rect2 = new Rect(10, 10, 20, 20);
- w2.setKeepClearAreas(Arrays.asList(rect2));
+ w2.setKeepClearAreas(Arrays.asList(rect2), Collections.emptyList());
// No keep clear areas on display, because the windows are not visible
assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index e3876150bf99..90a6918644fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -30,7 +30,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -182,12 +181,13 @@ public class InsetsStateControllerTest extends WindowTestsBase {
// Make IME and stay visible during the test.
mImeWindow.setHasSurface(true);
getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
- getController().onImeControlTargetChanged(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ getController().onImeControlTargetChanged(
+ mDisplayContent.getImeInputTarget().getWindowState());
final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
requestedVisibilities.setVisibility(ITYPE_IME, true);
- mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow()
+ mDisplayContent.getImeInputTarget().getWindowState()
.setRequestedVisibilities(requestedVisibilities);
- getController().onInsetsModified(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ getController().onInsetsModified(mDisplayContent.getImeInputTarget().getWindowState());
// Send our spy window (app) into the system so that we can detect the invocation.
final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e0911909f47e..3c3351c0b207 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
public void testCheckRotationAfterCleanup() {
mWm.setRecentsAnimationController(mController);
spyOn(mDisplayContent.mFixedRotationTransitionListener);
- doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
- .isTopFixedOrientationRecentsAnimating();
+ final ActivityRecord recents = mock(ActivityRecord.class);
+ recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ doReturn(ORIENTATION_PORTRAIT).when(recents)
+ .getRequestedConfigurationOrientation(anyBoolean());
+ mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
+
// Rotation update is skipped while the recents animation is running.
- assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
- .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
+ assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
+ assertEquals(recents.mOrientation, displayRotation.getLastOrientation());
final int prevRotation = mDisplayContent.getRotation();
mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
- waitHandlerIdle(mWm.mH);
+
+ // In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout
+ // -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent#
+ // applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG.
+ assertTrue(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
// The display should be updated to the changed orientation after the animation is finished.
- assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ assertNotEquals(displayRotation.getRotation(), prevRotation);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java
new file mode 100644
index 000000000000..1e3250080514
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.server.wm.UiDeviceUtils.pressUnlockButton;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
+import static android.server.wm.WindowManagerState.getLogicalDisplaySize;
+
+import android.app.KeyguardManager;
+import android.os.PowerManager;
+import android.view.SurfaceControl;
+import android.view.cts.surfacevalidator.CapturedActivity;
+import android.window.SurfaceSyncer;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import java.util.Objects;
+
+public class SurfaceSyncerContinuousTest {
+ @Rule
+ public TestName mName = new TestName();
+
+ @Rule
+ public ActivityTestRule<CapturedActivity> mActivityRule =
+ new ActivityTestRule<>(CapturedActivity.class);
+
+ public CapturedActivity mCapturedActivity;
+
+ @Before
+ public void setup() {
+ mCapturedActivity = mActivityRule.getActivity();
+ mCapturedActivity.setLogicalDisplaySize(getLogicalDisplaySize());
+
+ final KeyguardManager km = mCapturedActivity.getSystemService(KeyguardManager.class);
+ if (km != null && km.isKeyguardLocked() || !Objects.requireNonNull(
+ mCapturedActivity.getSystemService(PowerManager.class)).isInteractive()) {
+ pressWakeupButton();
+ pressUnlockButton();
+ }
+ }
+
+ @Test
+ public void testSurfaceViewSyncDuringResize() throws Throwable {
+ SurfaceSyncer.setTransactionFactory(SurfaceControl.Transaction::new);
+ mCapturedActivity.verifyTest(new SurfaceSyncerValidatorTestCase(), mName);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java
new file mode 100644
index 000000000000..cc28ea6f55d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.SurfaceSyncer;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@Presubmit
+public class SurfaceSyncerTest {
+ private SurfaceSyncer mSurfaceSyncer;
+
+ @Before
+ public void setup() {
+ mSurfaceSyncer = new SurfaceSyncer();
+ SurfaceSyncer.setTransactionFactory(StubTransaction::new);
+ }
+
+ @Test
+ public void testSyncOne() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+ Syncable syncable = new Syncable();
+ mSurfaceSyncer.addToSync(startSyncId, syncable);
+ mSurfaceSyncer.markSyncReady(startSyncId);
+
+ syncable.onBufferReady();
+
+ finishedLatch.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ @Test
+ public void testSyncMultiple() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+ Syncable syncable3 = new Syncable();
+
+ mSurfaceSyncer.addToSync(startSyncId, syncable1);
+ mSurfaceSyncer.addToSync(startSyncId, syncable2);
+ mSurfaceSyncer.addToSync(startSyncId, syncable3);
+ mSurfaceSyncer.markSyncReady(startSyncId);
+
+ syncable1.onBufferReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ syncable3.onBufferReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ syncable2.onBufferReady();
+
+ finishedLatch.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ @Test
+ public void testInvalidSyncId() {
+ assertFalse(mSurfaceSyncer.addToSync(0, new Syncable()));
+ }
+
+ @Test
+ public void testAddSyncWhenSyncComplete() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId, syncable1));
+ mSurfaceSyncer.markSyncReady(startSyncId);
+ // Adding to a sync that has been completed is also invalid since the sync id has been
+ // cleared.
+ assertFalse(mSurfaceSyncer.addToSync(startSyncId, syncable2));
+ }
+
+ @Test
+ public void testMultipleSyncSets() throws InterruptedException {
+ final CountDownLatch finishedLatch1 = new CountDownLatch(1);
+ final CountDownLatch finishedLatch2 = new CountDownLatch(1);
+ int startSyncId1 = mSurfaceSyncer.setupSync(transaction -> finishedLatch1.countDown());
+ int startSyncId2 = mSurfaceSyncer.setupSync(transaction -> finishedLatch2.countDown());
+
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId1, syncable1));
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId2, syncable2));
+ mSurfaceSyncer.markSyncReady(startSyncId1);
+ mSurfaceSyncer.markSyncReady(startSyncId2);
+
+ syncable1.onBufferReady();
+
+ finishedLatch1.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch1.getCount());
+ assertNotEquals(0, finishedLatch2.getCount());
+
+ syncable2.onBufferReady();
+
+ finishedLatch2.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch2.getCount());
+ }
+
+ private static class Syncable implements SurfaceSyncer.SyncTarget {
+ private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback;
+
+ @Override
+ public void onReadyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ mSyncBufferCallback = syncBufferCallback;
+ }
+
+ void onBufferReady() {
+ SurfaceControl.Transaction t = new StubTransaction();
+ mSyncBufferCallback.onBufferReady(t);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java
new file mode 100644
index 000000000000..77a8615e20d6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase;
+import android.view.cts.surfacevalidator.PixelChecker;
+import android.widget.FrameLayout;
+import android.window.SurfaceSyncer;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A validator class that will create a SurfaceView and then update its size over and over. The code
+ * will request to sync the SurfaceView content with the main window and validate that there was
+ * never an empty area (black color). The test uses {@link SurfaceSyncer} class to gather the
+ * content it wants to synchronize.
+ */
+public class SurfaceSyncerValidatorTestCase implements ISurfaceValidatorTestCase {
+ private static final String TAG = "SurfaceSyncerValidatorTestCase";
+
+ private final Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ updateSurfaceViewSize();
+ mHandler.postDelayed(this, 100);
+ }
+ };
+
+ private Handler mHandler;
+ private SurfaceView mSurfaceView;
+ private boolean mLastExpanded = true;
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+
+ private RenderingThread mRenderingThread;
+ private FrameLayout mParent;
+
+ private int mLastSyncId = -1;
+
+ final SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ final Canvas canvas = holder.lockCanvas();
+ canvas.drawARGB(255, 100, 100, 100);
+ holder.unlockCanvasAndPost(canvas);
+ Log.d(TAG, "surfaceCreated");
+ mRenderingThread = new RenderingThread(holder);
+ mRenderingThread.start();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
+ if (mLastSyncId >= 0) {
+ mSurfaceSyncer.addToSync(mLastSyncId, mSurfaceView, frameCallback ->
+ mRenderingThread.setFrameCallback(frameCallback));
+ mSurfaceSyncer.markSyncReady(mLastSyncId);
+ mLastSyncId = -1;
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mRenderingThread.stopRendering();
+ }
+ };
+
+ @Override
+ public PixelChecker getChecker() {
+ return new PixelChecker(Color.BLACK) {
+ @Override
+ public boolean checkPixels(int matchingPixelCount, int width, int height) {
+ return matchingPixelCount == 0;
+ }
+ };
+ }
+
+ @Override
+ public void start(Context context, FrameLayout parent) {
+ mSurfaceView = new SurfaceView(context);
+ mSurfaceView.getHolder().addCallback(mCallback);
+ mParent = parent;
+
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(MATCH_PARENT, 600);
+ parent.addView(mSurfaceView, layoutParams);
+ mHandler = new Handler(Looper.getMainLooper());
+ mHandler.post(mRunnable);
+ }
+
+ @Override
+ public void end() {
+ mHandler.removeCallbacks(mRunnable);
+ }
+
+ public void updateSurfaceViewSize() {
+ if (mRenderingThread == null || mLastSyncId >= 0 || !mRenderingThread.isReadyToSync()) {
+ return;
+ }
+
+ Log.d(TAG, "updateSurfaceViewSize");
+
+ final int height;
+ if (mLastExpanded) {
+ height = 300;
+ } else {
+ height = 600;
+ }
+ mLastExpanded = !mLastExpanded;
+
+ mRenderingThread.pauseRendering();
+ mLastSyncId = mSurfaceSyncer.setupSync(() -> { });
+ mSurfaceSyncer.addToSync(mLastSyncId, mParent);
+
+ ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
+ svParams.height = height;
+ mSurfaceView.setLayoutParams(svParams);
+ }
+
+ private static class RenderingThread extends HandlerThread {
+ private final SurfaceHolder mSurfaceHolder;
+ private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private boolean mPauseRendering;
+ private boolean mComplete;
+
+ int mColorValue = 0;
+ int mColorDelta = 10;
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ sleep(10);
+ synchronized (this) {
+ if (mComplete) {
+ break;
+ }
+ if (mPauseRendering) {
+ continue;
+ }
+
+ if (mFrameCallback != null) {
+ Log.d(TAG, "onFrameStarted");
+ mFrameCallback.onFrameStarted();
+ }
+
+ mColorValue += mColorDelta;
+ if (mColorValue > 245 || mColorValue < 10) {
+ mColorDelta *= -1;
+ }
+
+ Canvas c = mSurfaceHolder.lockCanvas();
+ if (c != null) {
+ c.drawRGB(255, mColorValue, 255 - mColorValue);
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ }
+
+ if (mFrameCallback != null) {
+ Log.d(TAG, "onFrameComplete");
+ mFrameCallback.onFrameComplete();
+ }
+
+ mFrameCallback = null;
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+
+ RenderingThread(SurfaceHolder holder) {
+ super("RenderingThread");
+ mSurfaceHolder = holder;
+ }
+
+ public void pauseRendering() {
+ synchronized (this) {
+ mPauseRendering = true;
+ }
+ }
+
+ private boolean isReadyToSync() {
+ synchronized (this) {
+ return mFrameCallback == null;
+ }
+ }
+ public void setFrameCallback(SurfaceSyncer.SurfaceViewFrameCallback frameCallback) {
+ synchronized (this) {
+ mFrameCallback = frameCallback;
+ mPauseRendering = false;
+ }
+ }
+
+ public void stopRendering() {
+ synchronized (this) {
+ mComplete = true;
+ }
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 29289a404791..501f0c4ac1b3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,6 +19,8 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -32,13 +34,17 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.isIndependent;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -557,11 +563,20 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testAppTransitionWithRotationChange() {
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
+ if (useFixedRotation) {
+ testFixedRotationOpen(player);
+ } else {
+ testShellRotationOpen(player);
+ }
+ }
+
+ private void testShellRotationOpen(TestTransitionPlayer player) {
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
final ActivityRecord app = createActivityRecord(mDisplayContent);
- final TestTransitionPlayer player = registerTestTransitionPlayer();
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
app.mTransitionController.requestStartTransition(transition, app.getTask(),
null /* remoteTransition */, null /* displayChange */);
@@ -605,6 +620,85 @@ public class TransitionTests extends WindowTestsBase {
assertNull(mDisplayContent.getAsyncRotationController());
}
+ private void testFixedRotationOpen(TestTransitionPlayer player) {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ app.mTransitionController.collectExistenceChange(app.getTask());
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
+ final AsyncRotationController asyncRotationController =
+ mDisplayContent.getAsyncRotationController();
+ assertNotNull(asyncRotationController);
+ assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertTrue(app.getTask().inTransition());
+
+ player.start();
+ player.finish();
+ app.getTask().clearSyncState();
+
+ // The open transition is finished. Continue to play seamless display change transition,
+ // so the previous async rotation controller should still exist.
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ mDisplayContent.setLastHasContent();
+ mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
+ assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
+ assertNotNull(mDisplayContent.getAsyncRotationController());
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+
+ onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
+ mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
+ player.finish();
+
+ // The controller should be cleared if the target windows are drawn.
+ statusBar.finishDrawing(mWm.mTransactionFactory.get());
+ statusBar.setOrientationChanging(false);
+ assertNull(mDisplayContent.getAsyncRotationController());
+ }
+
+ @Test
+ public void testDeferRotationForTransientLaunch() {
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
+ final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord home = new ActivityBuilder(mAtm)
+ .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
+ .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
+ final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
+ final int prevRotation = mDisplayContent.getRotation();
+ transition.setTransientLaunch(home, null /* restoreBelow */);
+ home.mTransitionController.requestStartTransition(transition, home.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ transition.collectExistenceChange(home);
+ home.mVisibleRequested = true;
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
+ doReturn(true).when(home).hasFixedRotationTransform(any());
+ player.startTransition();
+ player.onTransactionReady(mDisplayContent.getSyncTransaction());
+
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ spyOn(displayRotation);
+ doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+ doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
+ anyInt() /* orientation */, anyInt() /* lastRotation */);
+ // Rotation update is skipped while the recents animation is running.
+ assertFalse(mDisplayContent.updateRotationUnchecked());
+ assertEquals(SCREEN_ORIENTATION_NOSENSOR, displayRotation.getLastOrientation());
+ // Return to the app without fixed orientation from recents.
+ app.moveFocusableActivityToTop("test");
+ player.finish();
+ // The display should be updated to the changed orientation after the animation is finish.
+ assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ }
+
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
index 338555e4c9fc..7d2e9bf26b01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
@@ -30,6 +30,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static org.junit.Assert.assertEquals;
@@ -330,6 +331,23 @@ public class WindowLayoutTests {
}
@Test
+ public void layoutExtendedToDisplayCutout() {
+ addDisplayCutout();
+ final int height = DISPLAY_HEIGHT / 2;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ mAttrs.height = height;
+ mAttrs.gravity = Gravity.TOP;
+ mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mAttrs.setFitInsetsTypes(0);
+ mAttrs.privateFlags |= PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
+ computeFrames();
+
+ assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayFrame);
+ assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mParentFrame);
+ assertRect(0, 0, DISPLAY_WIDTH, height + DISPLAY_CUTOUT_HEIGHT, mFrame);
+ }
+
+ @Test
public void layoutInDisplayCutoutModeDefaultWithInvisibleSystemBars() {
addDisplayCutout();
mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
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 4d5fb6dc5813..25d7334a529c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -30,6 +30,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -610,6 +611,50 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
}
+ @Test
+ public void testAddRectInsetsProvider() {
+ final Task rootTask = createTask(mDisplayContent);
+
+ final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
+ navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
+ 0, 200, 1080, 700));
+
+ final Rect navigationBarInsetsProviderRect = new Rect(0, 0, 1080, 200);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.addRectInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
+ .toWindowContainerToken(), navigationBarInsetsProviderRect,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+ assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders
+ .valueAt(0).getSource().getType()).isEqualTo(ITYPE_LOCAL_NAVIGATION_BAR_1);
+ }
+
+ @Test
+ public void testRemoveInsetsProvider() {
+ final Task rootTask = createTask(mDisplayContent);
+
+ final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
+ navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
+ 0, 200, 1080, 700));
+
+ final Rect navigationBarInsetsProviderRect = new Rect(0, 0, 1080, 200);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.addRectInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
+ .toWindowContainerToken(), navigationBarInsetsProviderRect,
+ new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+ final WindowContainerTransaction wct2 = new WindowContainerTransaction();
+ wct2.removeInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
+ .toWindowContainerToken(), new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2);
+
+ assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders.size()).isEqualTo(0);
+ }
+
@UseTestDisplay
@Test
public void testTaskInfoCallback() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index ef600f085e50..a554fab76c2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -97,7 +97,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -992,14 +994,18 @@ public class WindowStateTests extends WindowTestsBase {
final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
final Rect keepClearArea2 = new Rect(5, 10, 15, 20);
final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2);
- window.setKeepClearAreas(keepClearAreas);
+ window.setKeepClearAreas(keepClearAreas, Collections.emptyList());
// Test that the keep-clear rects are stored and returned
- assertEquals(new ArraySet(keepClearAreas), new ArraySet(window.getKeepClearAreas()));
+ final List<Rect> windowKeepClearAreas = new ArrayList();
+ window.getKeepClearAreas(windowKeepClearAreas, new ArrayList());
+ assertEquals(new ArraySet(keepClearAreas), new ArraySet(windowKeepClearAreas));
// Test that keep-clear rects are overwritten
- window.setKeepClearAreas(Arrays.asList());
- assertEquals(0, window.getKeepClearAreas().size());
+ window.setKeepClearAreas(Collections.emptyList(), Collections.emptyList());
+ windowKeepClearAreas.clear();
+ window.getKeepClearAreas(windowKeepClearAreas, new ArrayList());
+ assertEquals(0, windowKeepClearAreas.size());
// Move the window position
final SurfaceControl.Transaction t = spy(StubTransaction.class);
@@ -1010,13 +1016,60 @@ public class WindowStateTests extends WindowTestsBase {
assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition);
// Test that the returned keep-clear rects are translated to display space
- window.setKeepClearAreas(keepClearAreas);
+ window.setKeepClearAreas(keepClearAreas, Collections.emptyList());
Rect expectedArea1 = new Rect(keepClearArea1);
expectedArea1.offset(frame.left, frame.top);
Rect expectedArea2 = new Rect(keepClearArea2);
expectedArea2.offset(frame.left, frame.top);
+ windowKeepClearAreas.clear();
+ window.getKeepClearAreas(windowKeepClearAreas, new ArrayList());
assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
- new ArraySet(window.getKeepClearAreas()));
+ new ArraySet(windowKeepClearAreas));
+ }
+
+ @Test
+ public void testUnrestrictedKeepClearAreas() {
+ final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ makeWindowVisible(window);
+
+ final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
+ final Rect keepClearArea2 = new Rect(5, 10, 15, 20);
+ final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2);
+ window.setKeepClearAreas(Collections.emptyList(), keepClearAreas);
+
+ // Test that the keep-clear rects are stored and returned
+ final List<Rect> restrictedKeepClearAreas = new ArrayList();
+ final List<Rect> unrestrictedKeepClearAreas = new ArrayList();
+ window.getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas);
+ assertEquals(Collections.emptySet(), new ArraySet(restrictedKeepClearAreas));
+ assertEquals(new ArraySet(keepClearAreas), new ArraySet(unrestrictedKeepClearAreas));
+
+ // Test that keep-clear rects are overwritten
+ window.setKeepClearAreas(Collections.emptyList(), Collections.emptyList());
+ unrestrictedKeepClearAreas.clear();
+ window.getKeepClearAreas(unrestrictedKeepClearAreas, new ArrayList());
+ assertEquals(0, unrestrictedKeepClearAreas.size());
+
+ // Move the window position
+ final SurfaceControl.Transaction t = spy(StubTransaction.class);
+ window.mSurfaceControl = mock(SurfaceControl.class);
+ final Rect frame = window.getFrame();
+ frame.set(10, 20, 60, 80);
+ window.updateSurfacePosition(t);
+ assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition);
+
+ // Test that the returned keep-clear rects are translated to display space
+ window.setKeepClearAreas(Collections.emptyList(), keepClearAreas);
+ Rect expectedArea1 = new Rect(keepClearArea1);
+ expectedArea1.offset(frame.left, frame.top);
+ Rect expectedArea2 = new Rect(keepClearArea2);
+ expectedArea2.offset(frame.left, frame.top);
+
+ unrestrictedKeepClearAreas.clear();
+ window.getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas);
+ assertEquals(Collections.emptySet(), new ArraySet(restrictedKeepClearAreas));
+ assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
+ new ArraySet(unrestrictedKeepClearAreas));
}
}
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
new file mode 100644
index 000000000000..bfc17713b6a8
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import static android.app.ActivityManager.procStateToString;
+
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_CANCELLED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_POSTED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_UPDATED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.TAG;
+import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessState;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
+import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
+
+public class BroadcastResponseStatsLogger {
+
+ private static final int MAX_LOG_SIZE =
+ ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final LogBuffer mBroadcastEventsBuffer = new LogBuffer(
+ BroadcastEvent.class, MAX_LOG_SIZE);
+ @GuardedBy("mLock")
+ private final LogBuffer mNotificationEventsBuffer = new LogBuffer(
+ NotificationEvent.class, MAX_LOG_SIZE);
+
+ void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
+ UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
+ synchronized (mLock) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, getBroadcastDispatchEventLog(sourceUid, targetPackage,
+ targetUser.getIdentifier(), idForResponseEvent, timeStampMs,
+ targetUidProcessState));
+ }
+ mBroadcastEventsBuffer.logBroadcastDispatchEvent(sourceUid, targetPackage,
+ targetUser, idForResponseEvent, timeStampMs, targetUidProcessState);
+ }
+ }
+
+ void logNotificationEvent(@NotificationEventType int event,
+ @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ synchronized (mLock) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, getNotificationEventLog(event, packageName, user.getIdentifier(),
+ timestampMs));
+ }
+ mNotificationEventsBuffer.logNotificationEvent(event, packageName, user, timestampMs);
+ }
+ }
+
+ void dumpLogs(IndentingPrintWriter ipw) {
+ synchronized (mLock) {
+ ipw.println("Broadcast events (most recent first):");
+ ipw.increaseIndent();
+ mBroadcastEventsBuffer.reverseDump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.println();
+ ipw.println("Notification events (most recent first):");
+ ipw.increaseIndent();
+ mNotificationEventsBuffer.reverseDump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+
+ private static final class LogBuffer<T extends Data> extends RingBuffer<T> {
+
+ LogBuffer(Class<T> classType, int capacity) {
+ super(classType, capacity);
+ }
+
+ void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
+ UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ final BroadcastEvent event = (BroadcastEvent) data;
+ event.sourceUid = sourceUid;
+ event.targetUserId = targetUser.getIdentifier();
+ event.targetUidProcessState = targetUidProcessState;
+ event.targetPackage = targetPackage;
+ event.idForResponseEvent = idForResponseEvent;
+ event.timestampMs = timeStampMs;
+ }
+
+ void logNotificationEvent(@NotificationEventType int type,
+ @NonNull String packageName, UserHandle user,
+ @ElapsedRealtimeLong long timestampMs) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ final NotificationEvent event = (NotificationEvent) data;
+ event.type = type;
+ event.packageName = packageName;
+ event.userId = user.getIdentifier();
+ event.timestampMs = timestampMs;
+ }
+
+ public void reverseDump(IndentingPrintWriter pw) {
+ final Data[] allData = toArray();
+ for (int i = allData.length - 1; i >= 0; --i) {
+ if (allData[i] == null) {
+ continue;
+ }
+ pw.println(getContent(allData[i]));
+ }
+ }
+
+ @NonNull
+ public String getContent(Data data) {
+ return data.toString();
+ }
+ }
+
+ @NonNull
+ private static String getBroadcastDispatchEventLog(int sourceUid, @NonNull String targetPackage,
+ @UserIdInt int targetUserId, long idForResponseEvent,
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
+ return TextUtils.formatSimple(
+ "broadcast:%s; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, state=%s",
+ TimeUtils.formatDuration(timestampMs), sourceUid, targetPackage, targetUserId,
+ idForResponseEvent, procStateToString(targetUidProcState));
+ }
+
+ @NonNull
+ private static String getNotificationEventLog(@NotificationEventType int event,
+ @NonNull String packageName, @UserIdInt int userId,
+ @ElapsedRealtimeLong long timestampMs) {
+ return TextUtils.formatSimple("notification:%s; event=<%s>, pkg=%s, usr=%d",
+ TimeUtils.formatDuration(timestampMs), notificationEventToString(event),
+ packageName, userId);
+ }
+
+ @NonNull
+ private static String notificationEventToString(@NotificationEventType int event) {
+ switch (event) {
+ case NOTIFICATION_EVENT_TYPE_POSTED:
+ return "posted";
+ case NOTIFICATION_EVENT_TYPE_UPDATED:
+ return "updated";
+ case NOTIFICATION_EVENT_TYPE_CANCELLED:
+ return "cancelled";
+ default:
+ return String.valueOf(event);
+ }
+ }
+
+ public static final class BroadcastEvent implements Data {
+ public int sourceUid;
+ public int targetUserId;
+ public int targetUidProcessState;
+ public String targetPackage;
+ public long idForResponseEvent;
+ public long timestampMs;
+
+ @Override
+ public void reset() {
+ targetPackage = null;
+ }
+
+ @Override
+ public String toString() {
+ return getBroadcastDispatchEventLog(sourceUid, targetPackage, targetUserId,
+ idForResponseEvent, timestampMs, targetUidProcessState);
+ }
+ }
+
+ public static final class NotificationEvent implements Data {
+ public int type;
+ public String packageName;
+ public int userId;
+ public long timestampMs;
+
+ @Override
+ public void reset() {
+ packageName = null;
+ }
+
+ @Override
+ public String toString() {
+ return getNotificationEventLog(type, packageName, userId, timestampMs);
+ }
+ }
+
+ public interface Data {
+ void reset();
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index ab8f69b121a0..76d2fe7917fc 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -16,10 +16,6 @@
package com.android.server.usage;
-import static android.app.ActivityManager.procStateToString;
-
-import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
-
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -29,11 +25,9 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -44,19 +38,19 @@ import java.util.ArrayList;
import java.util.List;
class BroadcastResponseStatsTracker {
- private static final String TAG = "ResponseStatsTracker";
+ static final String TAG = "ResponseStatsTracker";
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"NOTIFICATION_EVENT"}, value = {
- NOTIFICATION_EVENT_POSTED,
- NOTIFICATION_EVENT_UPDATED,
- NOTIFICATION_EVENT_CANCELLED
+ @IntDef(prefix = {"NOTIFICATION_EVENT_TYPE_"}, value = {
+ NOTIFICATION_EVENT_TYPE_POSTED,
+ NOTIFICATION_EVENT_TYPE_UPDATED,
+ NOTIFICATION_EVENT_TYPE_CANCELLED
})
- public @interface NotificationEvent {}
+ public @interface NotificationEventType {}
- private static final int NOTIFICATION_EVENT_POSTED = 0;
- private static final int NOTIFICATION_EVENT_UPDATED = 1;
- private static final int NOTIFICATION_EVENT_CANCELLED = 2;
+ static final int NOTIFICATION_EVENT_TYPE_POSTED = 0;
+ static final int NOTIFICATION_EVENT_TYPE_UPDATED = 1;
+ static final int NOTIFICATION_EVENT_TYPE_CANCELLED = 2;
private final Object mLock = new Object();
@@ -76,21 +70,19 @@ class BroadcastResponseStatsTracker {
new SparseArray<>();
private AppStandbyInternal mAppStandby;
+ private BroadcastResponseStatsLogger mLogger;
BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
mAppStandby = appStandby;
+ mLogger = new BroadcastResponseStatsLogger();
}
// TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
@ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
- if (DEBUG_RESPONSE_STATS) {
- Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; "
- + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s",
- sourceUid, targetPackage, targetUser, idForResponseEvent,
- TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState)));
- }
+ mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser,
+ idForResponseEvent, timestampMs, targetUidProcState);
if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
// No need to track the broadcast response state while the target app is
// in the foreground.
@@ -110,29 +102,23 @@ class BroadcastResponseStatsTracker {
void reportNotificationPosted(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_POSTED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_POSTED, packageName, user, timestampMs);
}
void reportNotificationUpdated(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_UPDATED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_UPDATED, packageName, user, timestampMs);
}
void reportNotificationCancelled(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_CANCELLED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_CANCELLED, packageName, user, timestampMs);
}
- private void reportNotificationEvent(@NotificationEvent int event,
+ private void reportNotificationEvent(@NotificationEventType int event,
@NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
- if (DEBUG_RESPONSE_STATS) {
- Slog.d(TAG, TextUtils.formatSimple(
- "reportNotificationEvent; event=<%s>, pkg=%s, usr=%d, ts=%s",
- notificationEventToString(event), packageName, user.getIdentifier(),
- TimeUtils.formatDuration(timestampMs)));
- }
- // TODO (206518114): Store last N events to dump for debugging purposes.
+ mLogger.logNotificationEvent(event, packageName, user, timestampMs);
synchronized (mLock) {
final LongSparseArray<BroadcastEvent> broadcastEvents =
getBroadcastEventsLocked(packageName, user);
@@ -157,13 +143,13 @@ class BroadcastResponseStatsTracker {
continue;
}
switch (event) {
- case NOTIFICATION_EVENT_POSTED:
+ case NOTIFICATION_EVENT_TYPE_POSTED:
responseStats.incrementNotificationsPostedCount(1);
break;
- case NOTIFICATION_EVENT_UPDATED:
+ case NOTIFICATION_EVENT_TYPE_UPDATED:
responseStats.incrementNotificationsUpdatedCount(1);
break;
- case NOTIFICATION_EVENT_CANCELLED:
+ case NOTIFICATION_EVENT_TYPE_CANCELLED:
responseStats.incrementNotificationsCancelledCount(1);
break;
default:
@@ -329,20 +315,6 @@ class BroadcastResponseStatsTracker {
return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
}
- @NonNull
- private String notificationEventToString(@NotificationEvent int event) {
- switch (event) {
- case NOTIFICATION_EVENT_POSTED:
- return "posted";
- case NOTIFICATION_EVENT_UPDATED:
- return "updated";
- case NOTIFICATION_EVENT_CANCELLED:
- return "cancelled";
- default:
- return String.valueOf(event);
- }
- }
-
void dump(@NonNull IndentingPrintWriter ipw) {
ipw.println("Broadcast response stats:");
ipw.increaseIndent();
@@ -351,6 +323,8 @@ class BroadcastResponseStatsTracker {
dumpBroadcastEventsLocked(ipw);
ipw.println();
dumpResponseStatsLocked(ipw);
+ ipw.println();
+ mLogger.dumpLogs(ipw);
}
ipw.decreaseIndent();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f70301735d9..1999cfc706b4 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -2187,7 +2187,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* @param uid Uid of the caller
*/
public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
- UsbUserPermissionManager permissions, int uid) {
+ UsbUserPermissionManager permissions, int pid, int uid) {
UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
if (currentAccessory == null) {
throw new IllegalArgumentException("no accessory attached");
@@ -2198,7 +2198,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
+ currentAccessory;
throw new IllegalArgumentException(error);
}
- permissions.checkPermission(accessory, uid);
+ permissions.checkPermission(accessory, pid, uid);
return nativeOpenAccessory();
}
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index f241e65ba755..9dda0e778394 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -98,7 +98,7 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
.checkPermission((UsbDevice) mDevice, packageName, pid, uid);
} else {
mPermissionManager.getPermissionsForUser(userId)
- .checkPermission((UsbAccessory) mDevice, uid);
+ .checkPermission((UsbAccessory) mDevice, pid, uid);
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c0ecf58087fa..e06ab022688f 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -321,6 +321,7 @@ public class UsbService extends IUsbManager.Stub {
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
int user = UserHandle.getUserId(uid);
final long ident = clearCallingIdentity();
@@ -328,7 +329,7 @@ public class UsbService extends IUsbManager.Stub {
synchronized (mLock) {
if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
return mDeviceManager.openAccessory(accessory, getPermissionsForUser(user),
- uid);
+ pid, uid);
} else {
Slog.w(TAG, "Cannot open " + accessory + " for user " + user
+ " as user is not active.");
@@ -505,11 +506,12 @@ public class UsbService extends IUsbManager.Stub {
@Override
public boolean hasAccessoryPermission(UsbAccessory accessory) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- return getPermissionsForUser(userId).hasPermission(accessory, uid);
+ return getPermissionsForUser(userId).hasPermission(accessory, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -533,11 +535,12 @@ public class UsbService extends IUsbManager.Stub {
public void requestAccessoryPermission(
UsbAccessory accessory, String packageName, PendingIntent pi) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, uid);
+ getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 286cff90daab..dd5f153b2518 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -246,9 +246,13 @@ class UsbUserPermissionManager {
* @param uid to check permission for
* @return {@code true} if caller has permssion
*/
- boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
+ boolean hasPermission(@NonNull UsbAccessory accessory, int pid, int uid) {
synchronized (mLock) {
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ if (uid == Process.SYSTEM_UID
+ || mDisablePermissionDialogs
+ || mContext.checkPermission(
+ android.Manifest.permission.MANAGE_USB, pid, uid)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED) {
return true;
}
AccessoryFilter filter = new AccessoryFilter(accessory);
@@ -675,8 +679,8 @@ class UsbUserPermissionManager {
}
}
- public void checkPermission(UsbAccessory accessory, int uid) {
- if (!hasPermission(accessory, uid)) {
+ public void checkPermission(UsbAccessory accessory, int pid, int uid) {
+ if (!hasPermission(accessory, pid, uid)) {
throw new SecurityException("User has not given " + uid + " permission to accessory "
+ accessory);
}
@@ -745,9 +749,9 @@ class UsbUserPermissionManager {
}
public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
- int uid) {
+ int pid, int uid) {
// respond immediately if permission has already been granted
- if (hasPermission(accessory, uid)) {
+ if (hasPermission(accessory, pid, uid)) {
Intent intent = new Intent();
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f43e5aacad33..df114dbabe5b 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1292,31 +1292,22 @@ public class TelecomManager {
}
/**
- * Returns a list of {@link PhoneAccountHandle}s for self-managed {@link ConnectionService}s.
+ * Returns a list of {@link PhoneAccountHandle}s for all self-managed
+ * {@link ConnectionService}s owned by the calling {@link UserHandle}.
* <p>
* Self-Managed {@link ConnectionService}s have a {@link PhoneAccount} with
* {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
* <p>
* Requires permission {@link android.Manifest.permission#READ_PHONE_STATE}, or that the caller
- * is the default dialer app to get all phone account handles.
- * <P>
- * If the caller doesn't meet any of the above requirements and has {@link
- * android.Manifest.permission#MANAGE_OWN_CALLS}, the caller can get only the phone account
- * handles they have registered.
+ * is the default dialer app.
* <p>
- * A {@link SecurityException} will be thrown if the caller is not the default dialer
- * or the caller does not have at least one of the following permissions:
- * {@link android.Manifest.permission#READ_PHONE_STATE} permission,
- * {@link android.Manifest.permission#MANAGE_OWN_CALLS} permission
+ * A {@link SecurityException} will be thrown if a called is not the default dialer, or lacks
+ * the {@link android.Manifest.permission#READ_PHONE_STATE} permission.
*
* @return A list of {@code PhoneAccountHandle} objects.
*/
- @RequiresPermission(anyOf = {
- READ_PRIVILEGED_PHONE_STATE,
- android.Manifest.permission.READ_PHONE_STATE,
- android.Manifest.permission.MANAGE_OWN_CALLS
- })
- public List<PhoneAccountHandle> getSelfManagedPhoneAccounts() {
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public @NonNull List<PhoneAccountHandle> getSelfManagedPhoneAccounts() {
ITelecomService service = getTelecomService();
if (service != null) {
try {
@@ -1330,6 +1321,34 @@ public class TelecomManager {
}
/**
+ * Returns a list of {@link PhoneAccountHandle}s owned by the calling self-managed
+ * {@link ConnectionService}.
+ * <p>
+ * Self-Managed {@link ConnectionService}s have a {@link PhoneAccount} with
+ * {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
+ * <p>
+ * Requires permission {@link android.Manifest.permission#MANAGE_OWN_CALLS}
+ * <p>
+ * A {@link SecurityException} will be thrown if a caller lacks the
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} permission.
+ *
+ * @return A list of {@code PhoneAccountHandle} objects.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_OWN_CALLS)
+ public @NonNull List<PhoneAccountHandle> getOwnSelfManagedPhoneAccounts() {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getOwnSelfManagedPhoneAccounts(mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ throw new IllegalStateException("Telecom is not available");
+ }
+
+ /**
* Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
* by the user.
*
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b9936ce2e1b2..9999c897d1b6 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -66,6 +66,12 @@ interface ITelecomService {
String callingFeatureId);
/**
+ * @see TelecomServiceImpl#getOwnSelfManagedPhoneAccounts
+ */
+ List<PhoneAccountHandle> getOwnSelfManagedPhoneAccounts(String callingPackage,
+ String callingFeatureId);
+
+ /**
* @see TelecomManager#getPhoneAccountsSupportingScheme
*/
List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(in String uriScheme,
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 8d9252019538..7129a27c3cd6 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -29,7 +29,6 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.TelephonyManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
@@ -744,18 +743,16 @@ public abstract class EuiccService extends Service {
public void run() {
DownloadSubscriptionResult result;
try {
- result =
- EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim,
- resolvedBundle);
+ result = EuiccService.this.onDownloadSubscription(
+ slotId, portIndex, subscription, switchAfterDownload,
+ forceDeactivateSim, resolvedBundle);
} catch (AbstractMethodError e) {
- Log.w(TAG, "The new onDownloadSubscription(int, "
+ Log.w(TAG, "The new onDownloadSubscription(int, int, "
+ "DownloadableSubscription, boolean, boolean, Bundle) is not "
+ "implemented. Fall back to the old one.", e);
- int resultCode = EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim);
- result = new DownloadSubscriptionResult(resultCode,
- 0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
+ result = EuiccService.this.onDownloadSubscription(
+ slotId, subscription, switchAfterDownload,
+ forceDeactivateSim, resolvedBundle);
}
try {
callback.onComplete(result);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index adb51c4d0cff..837cf8b28ff8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3850,30 +3850,42 @@ public class CarrierConfigManager {
public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
"opportunistic_esim_download_via_wifi_only_bool";
- /**
- * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
+/**
+ * Controls RSRP threshold, in dBm, at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
"opportunistic_network_entry_threshold_rsrp_int";
/**
- * Controls RSSNR threshold at which OpportunisticNetworkService will decide whether
- * the opportunistic network is good enough for internet data.
+ * Controls RSSNR threshold, in dB, at which OpportunisticNetworkService will
+ * decide whether the opportunistic network is good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
"opportunistic_network_entry_threshold_rssnr_int";
/**
- * Controls RSRP threshold below which OpportunisticNetworkService will decide whether
+ * Controls RSRP threshold, in dBm, below which OpportunisticNetworkService will decide whether
* the opportunistic network available is not good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
"opportunistic_network_exit_threshold_rsrp_int";
/**
- * Controls RSSNR threshold below which OpportunisticNetworkService will decide whether
- * the opportunistic network available is not good enough for internet data.
+ * Controls RSSNR threshold, in dB, below which OpportunisticNetworkService will
+ * decide whether the opportunistic network available is not good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
"opportunistic_network_exit_threshold_rssnr_int";
@@ -3971,7 +3983,7 @@ public class CarrierConfigManager {
* good enough for internet data. Note other factors may be considered for the final
* decision.
*
- * <p>The value of {@link CellSignalStrengthNr#getSsRsrp} will be compared with this
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
* threshold.
*
* @hide
@@ -3998,7 +4010,7 @@ public class CarrierConfigManager {
* good enough for internet data. Note other factors may be considered for the final
* decision.
*
- * <p>The value of {@link CellSignalStrengthNr#getSsRsrq} will be compared with this
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
* threshold.
*
* @hide
@@ -4025,6 +4037,9 @@ public class CarrierConfigManager {
* be considered good enough for internet data. Note other factors may be considered
* for the final decision.
*
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
+ * threshold.
+ *
* @hide
*/
public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT =
@@ -4048,6 +4063,9 @@ public class CarrierConfigManager {
* be considered good enough for internet data. Note other factors may be considered
* for the final decision.
*
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
+ * threshold.
+ *
* @hide
*/
public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
@@ -8995,9 +9013,9 @@ public class CarrierConfigManager {
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
/* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 5);
/* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 1);
/* Default value is 1024 kbps */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
/* Default value is 10 seconds */
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 947dc0107dba..5e902613a654 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -125,13 +125,13 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
/**
* Construct a cell signal strength
*
- * @param rssi in dBm [-113,-51], UNKNOWN
- * @param rsrp in dBm [-140,-43], UNKNOWN
- * @param rsrq in dB [-34, 3], UNKNOWN
- * @param rssnr in dB [-20, +30], UNKNOWN
- * @param cqiTableIndex [1, 6], UNKNOWN
- * @param cqi [0, 15], UNKNOWN
- * @param timingAdvance [0, 1282], UNKNOWN
+ * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+ * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+ * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+ * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+ * @param cqiTableIndex [1, 6], {@link CellInfo#UNAVAILABLE}
+ * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+ * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
*
*/
/** @hide */
@@ -151,12 +151,12 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
/**
* Construct a cell signal strength
*
- * @param rssi in dBm [-113,-51], UNKNOWN
- * @param rsrp in dBm [-140,-43], UNKNOWN
- * @param rsrq in dB [-34, 3], UNKNOWN
- * @param rssnr in dB [-20, +30], UNKNOWN
- * @param cqi [0, 15], UNKNOWN
- * @param timingAdvance [0, 1282], UNKNOWN
+ * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+ * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+ * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+ * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+ * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+ * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
*
*/
/** @hide */
@@ -403,10 +403,11 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
}
/**
- * Get reference signal signal-to-noise ratio
+ * Get reference signal signal-to-noise ratio in dB
+ * Range: -20 dB to +30 dB.
*
* @return the RSSNR if available or
- * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ * {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable.
*/
public int getRssnr() {
return mRssnr;
@@ -414,8 +415,10 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
/**
* Get reference signal received power in dBm
+ * Range: -140 dBm to -43 dBm.
*
- * @return the RSRP of the measured cell.
+ * @return the RSRP of the measured cell or {@link CellInfo#UNAVAILABLE} if
+ * unavailable.
*/
public int getRsrp() {
return mRsrp;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8e10f6b8e69e..fc94ebf2cc37 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -369,6 +369,10 @@ public class ApnSetting implements Parcelable {
public @interface AuthType {}
// Possible values for protocol which is defined in TS 27.007 section 10.1.1.
+ /** Unknown protocol.
+ * @hide
+ */
+ public static final int PROTOCOL_UNKNOWN = -1;
/** Internet protocol. */
public static final int PROTOCOL_IP = 0;
/** Internet protocol, version 6. */
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index dd4a9a3a4d69..d5f92a3181b9 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -17,6 +17,7 @@
package android.test;
import android.accounts.AccountManager;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,6 +27,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Process;
import android.test.mock.MockAccountManager;
import java.io.File;
@@ -64,6 +66,15 @@ public class IsolatedContext extends ContextWrapper {
}
@Override
+ public AttributionSource getAttributionSource() {
+ AttributionSource attributionSource = super.getAttributionSource();
+ if (attributionSource == null) {
+ return new AttributionSource.Builder(Process.myUid()).build();
+ }
+ return attributionSource;
+ }
+
+ @Override
public ContentResolver getContentResolver() {
// We need to return the real resolver so that MailEngine.makeRight can get to the
// subscribed feeds provider. TODO: mock out subscribed feeds too.
diff --git a/tests/AttestationVerificationTest/Android.bp b/tests/AttestationVerificationTest/Android.bp
index a4741eedaac0..b98f8cb0c21d 100644
--- a/tests/AttestationVerificationTest/Android.bp
+++ b/tests/AttestationVerificationTest/Android.bp
@@ -40,5 +40,6 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"platform-test-annotations",
+ "services.core",
],
}
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
index c42bde9dca3a..37321ad80b0f 100755
--- a/tests/AttestationVerificationTest/AndroidManifest.xml
+++ b/tests/AttestationVerificationTest/AndroidManifest.xml
@@ -24,6 +24,7 @@
<application>
<uses-library android:name="android.test.runner"/>
<activity android:name=".SystemAttestationVerificationTest$TestActivity" />
+ <activity android:name=".PeerDeviceSystemAttestationVerificationTest$TestActivity" />
</application>
<!-- self-instrumenting test package. -->
diff --git a/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
new file mode 100644
index 000000000000..e29ff487806e
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIICkjCCAjmgAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQMDANURUUxKTAn
+BgNVBAUTIDg2ZTQ0MjRhMjY2NDlhZDcyZWZhNWM0MWEwM2IyN2QxMCAXDTcwMDEw
+MTAwMDAwMFoYDzIxMDYwMjA3MDYyODE1WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtl
+eXN0b3JlIEtleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIlTwcvhe+DLV45X
+RCTO7HoN20Ib7IbCEhV5+YdMiYOp/0AdKk8oYvsri1XODeC4zcoPfHNdQGt/68i0
+ADbilJmjggFIMIIBRDAOBgNVHQ8BAf8EBAMCB4AwggEwBgorBgEEAdZ5AgERBIIB
+IDCCARwCAQMKAQECAQQKAQEECXBsYXllcjQ1NgQAMFe/hT0IAgYBfvkgVei/hUVH
+BEUwQzEdMBsEFmNvbS5nb29nbGUuYXR0ZXN0YXRpb24CAQExIgQgOqyVXRJUdAGY
+/XVx8y/uRPiebqlyELt1EpqIz29h5tUwgaehCDEGAgECAgEDogMCAQOjBAICAQCl
+CDEGAgEEAgEGqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCCEZx8qY8Ys0HC2TqPq
+74eYPzh5L/agxD7Bn7zVBQHoNAEB/woBAAQguJwoDfWBjRaedzQ6TJPFJJKs+ytr
++8Vu2CSmqifFBHW/hUEFAgMB1MC/hUIFAgMDFdm/hU4GAgQBNIjJv4VPBgIEATSI
+yTAKBggqhkjOPQQDAgNHADBEAiBdGxfMEx59k5+zo+hV3Q9kgjbGi0zU3WH355P5
+JZttBwIgY4FZsSreUJL8RY3JvfvD8BRw8GuXcB1OQ600hwaYYC4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8zCCAXqgAwIBAgIRAOuuukN0OHbNQvKngECkewEwCgYIKoZIzj0EAwIwOTEM
+MAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYxMDRmYWFlOTQ3ODY0ZTU4
+MDRmMWY4ZDAeFw0yMDA5MjgyMDI3NTZaFw0zMDA5MjYyMDI3NTZaMDkxDDAKBgNV
+BAwMA1RFRTEpMCcGA1UEBRMgODZlNDQyNGEyNjY0OWFkNzJlZmE1YzQxYTAzYjI3
+ZDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT3Mjl05ewv6G8zAR4fXJy2iadU
+yK7rNvzlECy2+nhEieL8BFXDvo0tx5fYs8qr67j/KvluFBfp2r9s+ckWz3Kzo2Mw
+YTAdBgNVHQ4EFgQUsVKBzAs1lMXAauQ3mGAJZJqK5tAwHwYDVR0jBBgwFoAUEsQA
+i8d2oLULSi5Ix4BTGGbvUEkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AgQwCgYIKoZIzj0EAwIDZwAwZAIwfFziBCwuM1thLUSUNI61Xx/vnDnNkSv/aX5D
+yLjxbLlgnFSzIrc+6vf6h6L/+TjYAjAq6h9GKtMn4R0286MoqYqzp/rHn6JD2sqH
+iM8KZ0oA+Ut242EcmGjAoNfGZGZGddQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAXugAwIBAgIQNTAX5z3CBac6nD3hQiMDcDANBgkqhkiG9w0BAQsFADAb
+MRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MB4XDTIwMDkyODIwMjUwMloXDTMw
+MDkyNjIwMjUwMlowOTEMMAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYx
+MDRmYWFlOTQ3ODY0ZTU4MDRmMWY4ZDB2MBAGByqGSM49AgEGBSuBBAAiA2IABA/7
+xZFlFtTjdy2B3p7E+FsrBjyhBSqY4a9FywawXMJRSja3HAK36ruzJjWlEkD+D0vq
+HI2joY39FHmWoZWwm2cq9gOleFGYOSCpMr4ib7xtq/6nefvKTP5rutxudF97t6Nj
+MGEwHQYDVR0OBBYEFBLEAIvHdqC1C0ouSMeAUxhm71BJMB8GA1UdIwQYMBaAFDZh
+4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgIEMA0GCSqGSIb3DQEBCwUAA4ICAQAaMONDQxJz3PRn9gHQW5KP+TIoBPJZyGa1
+QFuEBcMDTtIxBxEh5Pj3ivPBc76PrdYu5U47Ve5YYCPsTpUTj7dOxbzGSZjfjvHF
+fNwy24g1Lah2iAdQRVErhWKBlpnQhBnnRrrNmTTmzhl8NvSExqAPP746dqwm1kQ7
+YesC5yoEAHpxamhlZpIKAjSxSZeHWace2qV00M8qWd/7lIpqttJjFFrhCjzR0dtr
+oIIpC5EtmqIWdLeg6yZjJkX+Cjv4F8mRfBtwuNuxFsfALQ3D5l8WKw3iwPebmCy1
+kEby8Eoq88FxzXQp/XgAaljlrKXyuxptrc1noRuob4g42Oh6wetueYRSCtO6Bkym
+0UMnld/kG77aeiHOMVVb86wrhNuAGir1vgDGOBsclITVyuu9ka0YVQjjDm3phTpd
+O8JV16gbei2Phn+FfRV1MSDsZo/wu0i2KVzgs27bfJocMHXv+GzvwfefYgMJ/rYq
+Bg27lpsWzmFEPv2cyhA5PwwbG8ceswa3RZE/2eS9o7STkz93jr/KsKLcMBY6cX2C
+q4CBJByKFJtVANOVj+neFNxc2sQgeTT33yYNKbe4b5bm7Ki1FbrhFVckpzUGDnKs
+gL+AxvALWOoryDGwNbJiW8PRiD3HHByiMvSEQ7e7BSc2KjbsaWbCfYZAMZJEhEsc
+P1l8lcUVuA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
+NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
+XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
+h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
+L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
+QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
+D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
+mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
+Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
+oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
+jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
+ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
+ex0SdDrx+tWUDqG8At2JHA==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
new file mode 100644
index 000000000000..3d6410af042a
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx
+MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb
+BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c
+NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU
+xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo
+/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+
+tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD
+AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB
+ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj
+aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl
+cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp
+Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv
+Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h
+bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y
+AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy
+b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB
+rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+
+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP
+HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC
+AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S
+OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+
+qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa
+u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3
+Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk
+DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW
+OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i
+3i9VM6yOLIrP
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
new file mode 100644
index 000000000000..6d261fae47cf
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoDCCA4igAwIBAgIQTfpKgAsLZJhp2V4xUriMADANBgkqhkiG9w0BAQ0FADBp
+MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFu
+ZHJvaWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlv
+biBSb290IENBMCAXDTE3MDYyMTIwMjQzN1oYDzIwNTcwNjExMjAyNDM3WjBpMQsw
+CQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFuZHJv
+aWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlvbiBS
+b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuO82oerGivb9
+G9bWyM8Pg0y6SOnAC8/8b92dp1v4Npnc+QpjPRUKgn8lzjQ9Jo6IGY3OShRBiQYl
+bbZYkfJnC5HtqbOETdPLZclErVE/G6Oda1IeZWvQVMjNImEYOLL5ct2RxiPttd8v
+SLyOSNFPf5/SeFqX/La0NcmXMOvPSrTW3qO34brnC+ih7mlpJFLz6Up93N3Umxsl
+IElz2wCG72t6k3+caWLyIPVgIPmsQrfTeBK/hN5dAJgAN65BsTevLHRP9J610wj3
+RagSIK1NdTuJRnr5ZyTQrwE2nA8H3IJ7/eo6IlGhXPwLKDhbdxYygPxdlCq6Rl96
+aVLjfpqDPtJ9ow+QKZuEDbYJ4z4olNXC6O5G7vqnCuULA/2E7y7DZObjzXOrdx2z
+9YKd8BrIDMTN/5mmw2us8tywiaQhbl8vOtjU+A+iBBnkj/wt9TYyLKopdrDlo5mz
+wy5l750HOkVZXC3VkeECnp+9duSHdS4qeUf/W1j9nPM7kY0HFLPUVX9AFVp2JXnC
+iKZC32GQAVsDc1iyAZWAVTqA7E0fBHhk9jUnA0W9b5Lq06oW95ngNR1MIFY871i8
+aLHCBpIix8DuMe8NB9spCIP6WCQqGiWQQpzbeuBPtoi424xwZTO4oectTd77bs9V
+Rvunn49fz308KnoWjk/by1N7gWyTb38CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMDQ1I0RKwFCI+Fy9uIIJ/HrXuqu
+MA0GCSqGSIb3DQEBDQUAA4ICAQB09qkyEpEDocdN5pPeXqtjj9d0AXREUGH2LhnC
+z9KZcUFR+JskwEMHCaOENOmKI3zWRmxT7d8cVywwGk+ExE7EBQoeHlh3Yo44M8aj
+ZL7RHCvHRYsePhAJkYpJ02IMR60TV+1jhMqE8+BnqFivS7kft4t295EyrnLRZE3b
+Nfc0t011j02RwUrioR57mdvS9EZBRnMQkobhn+jWt9O+V3mtplW+1A2n4ec6uni1
+2MMgAWHuO1sKVYd5Sp4JMUpNnfmQAMnNiOMF6VxkpaoF1lZWo4TrLxuDKJG3O8h1
+fByjCpNVY8kOvvYEadbldzh6Agy/3ppb9yfG7X7FtHr1ghNjuNT6w5VgvbRtoRja
+/ZSKuJMaKm5emMWNkls/cwVSPJIvTOzPTeYK1BKSyAL2LDJ93HI7x8h79/Q7gKRi
+kL8qT7GW2FqpWTK0253sJHqCJJP4A5Rxtf2+Afwqadfc6Ga4jJHb7rPXngz4j1ZB
+gl5yjXgWF9wHGxqrjKWe2EA3d47BC4HG3Rf5L56KQiRPhTqTk5vtZwtwLRLFDLt7
+Hdff13O1oLhn+2z9xkASUL3rFE/qWajZP7fk3CvzcuXwKDTZomIC4nNaglx4nLdj
+lHhOq+6ON8MZC46sLStD+D4a9A1HOoihJgI/yGGkwdrp4KQIveRkEBO/x9v3NNBE
+bMwG9w==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_root_certs.pem b/tests/AttestationVerificationTest/assets/test_root_certs.pem
new file mode 100644
index 000000000000..c51851fe3da5
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_root_certs.pem
@@ -0,0 +1,61 @@
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
+ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
+VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
+Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
+ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
+Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
+qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
+DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
+QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
+JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
+CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
+ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
+qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
+MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
+wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
+NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
+XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
+h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
+L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
+QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
+D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
+mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
+Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
+oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
+jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
+ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
+ex0SdDrx+tWUDqG8At2JHA==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
new file mode 100644
index 000000000000..282771000bf7
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
@@ -0,0 +1,50 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCApGgAwIBAgIBATAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCVVMxEzAR
+BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UE
+CwwHQW5kcm9pZDE7MDkGA1UEAwwyQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBB
+dHRlc3RhdGlvbiBJbnRlcm1lZGlhdGUwHhcNNzAwMTAxMDAwMDAwWhcNNjkxMjMx
+MjM1OTU5WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtleXN0b3JlIEtleTBZMBMGByqG
+SM49AgEGCCqGSM49AwEHA0IABEYtCH28qu+St0F0TixVsQz0L/Y7DcRHgYAU98E6
+edwOpACFmmseYxMjvmZv/4jURSG2/Z0J1s3A/qFzIY96/tyjggFSMIIBTjALBgNV
+HQ8EBAMCB4AwggEcBgorBgEEAdZ5AgERBIIBDDCCAQgCAQQKAQACASkKAQAECXBs
+YXllcjQ1NgQAMIHqoQgxBgIBAgIBA6IDAgEDowQCAgEApQgxBgIBBAIBBqoDAgEB
+v4N3AgUAv4U9CAIGAX8DoY9Qv4U+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAv4VBBQIDAa2wv4VCBQIDAxUbv4VFRwRFMEMxHTAbBBZjb20uZ29v
+Z2xlLmF0dGVzdGF0aW9uAgEBMSIEIDqslV0SVHQBmP11cfMv7kT4nm6pchC7dRKa
+iM9vYebVMAAwHwYDVR0jBBgwFoAUP/ys1hqxOp6BILjVJRzFZbsekakwCgYIKoZI
+zj0EAwIDSQAwRgIhAMzs7gWWBIITpeLeEEx9B8ihdhkFqpMGlsYLRO01ZIOeAiEA
+uKs9xfK3fIOpVAhDmsrp+zE8KUwyvqCU/IS13tXz7Ng=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
+VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
+ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
+MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
+bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
+dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
+efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
+U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
+qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
+wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
+Xvsiu+f+uXc/WT/7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
+dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
+VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
+HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
+QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
+dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
+BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
+EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
+SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
+C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
new file mode 100644
index 000000000000..32c2230e4880
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
@@ -0,0 +1,161 @@
+package android.security.attestationverification
+
+import android.app.Activity
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_UNKNOWN
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.ByteArrayOutputStream
+import java.security.cert.CertificateFactory
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PeerDeviceSystemAttestationVerificationTest {
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ private val certifcateFactory = CertificateFactory.getInstance("X.509")
+ private lateinit var activity: Activity
+ private lateinit var avm: AttestationVerificationManager
+ private lateinit var invalidAttestationByteArray: ByteArray
+
+ @Before
+ fun setup() {
+ rule.getScenario().onActivity {
+ avm = it.getSystemService(AttestationVerificationManager::class.java)
+ activity = it
+ }
+ invalidAttestationByteArray = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToByteArray()
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_UNKNOWN, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyRequirements() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureMismatchBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val publicKeyRequirements = Bundle()
+ publicKeyRequirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, publicKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+
+ val future2 = CompletableFuture<Int>()
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "challengeStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future2.complete(result)
+ }
+
+ assertThat(future2.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongResourceKey() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val wrongKeyRequirements = Bundle()
+ wrongKeyRequirements.putByteArray("wrongReqKey", "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, wrongKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyAttestation() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val requirements = Bundle()
+ requirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, requirements, ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustAnchorMismatch() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun <T> CompletableFuture<T>.getSoon(): T {
+ return this.get(1, TimeUnit.SECONDS)
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = certifcateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
index 62902929fcd5..169effaa45ca 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -12,8 +12,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import com.google.common.truth.Truth.assertThat
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
-import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN
import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
@@ -52,7 +52,7 @@ class SystemAttestationVerificationTest {
@Test
fun verifyAttestation_returnsUnknown() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_UNKNOWN)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { result, _ ->
future.complete(result)
@@ -137,7 +137,7 @@ class SystemAttestationVerificationTest {
@Test
fun verifyToken_returnsUnknown() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
@@ -150,7 +150,7 @@ class SystemAttestationVerificationTest {
@Test
fun verifyToken_tooBigMaxAgeThrows() {
val future = CompletableFuture<VerificationToken>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
future.complete(token)
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
new file mode 100644
index 000000000000..0d15fe72920a
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/** Test for data class holding parsed X509Certificate attestation attributes. */
+@RunWith(AndroidJUnit4.class)
+public class AndroidKeystoreAttestationVerificationAttributesTest {
+ @Rule public ExpectedException mException = ExpectedException.none();
+ private static final String TEST_PHYSCIAL_DEVICE_CERTS =
+ "test_attestation_wrong_root_certs.pem";
+ private static final String TEST_PHYSICAL_DEVICE_CERTS_2 =
+ "test_attestation_with_root_certs.pem";
+ private static final String TEST_VIRTUAL_DEVICE_CERTS =
+ "test_virtual_device_attestation_certs.pem";
+ private static final String TEST_CERT_NO_ATTESTATION_EXTENSION =
+ "test_no_attestation_ext_certs.pem";
+ private static final String TEST_CERTS_NO_ATTESTATION_EXTENSION_2 =
+ "test_root_certs.pem";
+
+
+ private CertificateFactory mFactory;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes2;
+ private AndroidKeystoreAttestationVerificationAttributes mVirtualDeviceAttributes;
+
+ @Before
+ public void setUp() throws Exception {
+ mFactory = CertificateFactory.getInstance("X.509");
+ mPhysicalDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificate(TEST_PHYSCIAL_DEVICE_CERTS));
+ mPhysicalDeviceAttributes2 =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_PHYSICAL_DEVICE_CERTS_2).get(0));
+ mVirtualDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_VIRTUAL_DEVICE_CERTS).get(0));
+ }
+
+ @Test
+ public void parseCertificate_noAttestationExtension() throws Exception {
+ List<X509Certificate> certsNoAttestation =
+ generateCertificates(TEST_CERTS_NO_ATTESTATION_EXTENSION_2);
+ certsNoAttestation.add(generateCertificate(TEST_CERT_NO_ATTESTATION_EXTENSION));
+ for (X509Certificate cert: certsNoAttestation) {
+ mException.expect(CertificateEncodingException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("No attestation extension found in certificate."));
+
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(cert);
+ }
+ }
+
+ @Test
+ public void parseCertificate_attestationLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationVersion()).isEqualTo(3);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationVersion()).isEqualTo(3);
+ assertThat(mVirtualDeviceAttributes.getAttestationVersion()).isEqualTo(4);
+ }
+
+ @Test
+ public void parseCertificate_attestationSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isAttestationHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isAttestationHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isAttestationHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isAttestationHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keymasterLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mVirtualDeviceAttributes.getKeymasterVersion()).isEqualTo(41);
+ }
+
+ @Test
+ public void parseCertificate_keymasterSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isKeymasterHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isKeymasterHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_attestationChallenge() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "abc".getBytes(UTF_8));
+ assertThat(mPhysicalDeviceAttributes2.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ assertThat(mVirtualDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootState() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.UNVERIFIED);
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED);
+ assertThat(mVirtualDeviceAttributes.getVerifiedBootState()).isNull();
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyBootPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyBootPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyBootPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyBootPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsPatchLevel()).isEqualTo(202201);
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyVendorPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyVendorPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyVendorPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyVendorPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyAuthenticatorType() {
+ assertThat(mPhysicalDeviceAttributes.getKeyAuthenticatorType()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyAuthenticatorType()).isEqualTo(0);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersion() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsVersion()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsVersion()).isEqualTo(120000);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersionNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsVersion is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsVersion();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootHash() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootHash()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootHash()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootKey() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootKey()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootKey()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLocked() {
+ assertThat(mPhysicalDeviceAttributes.isVerifiedBootLocked()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.isVerifiedBootLocked()).isTrue();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLockedNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("VerifiedBootLocked is not set."));
+
+ mVirtualDeviceAttributes.isVerifiedBootLocked();
+ }
+
+ @Test
+ public void parseCertificate_applicationPackageNameVersion() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationPackageNameVersion()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_applicationCertificateDigests() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationCertificateDigests()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_valuesNotSet() {
+ assertThat(mPhysicalDeviceAttributes.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.isKeyAllowedForAllApplications()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.isKeyAllowedForAllApplications()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keyRequiresUnlockedDeviceNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyRequiresUnlockedDevice is not set."));
+
+ mPhysicalDeviceAttributes.isKeyRequiresUnlockedDevice();
+ }
+
+ private X509Certificate generateCertificate(String certificateString)
+ throws Exception {
+ return generateCertificates(certificateString).get(0);
+ }
+
+ private List<X509Certificate> generateCertificates(String certificateString)
+ throws Exception {
+ Collection<? extends Certificate> certificates = mFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(certificateString));
+
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
+ for (Certificate cert : certificates) {
+ x509Certs.add((X509Certificate) cert);
+ }
+ return x509Certs;
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
new file mode 100644
index 000000000000..45f2e5c6fdf7
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -0,0 +1,175 @@
+package com.android.server.security
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import java.io.ByteArrayOutputStream
+import java.security.cert.Certificate
+import java.security.cert.CertificateFactory
+import java.security.cert.TrustAnchor
+import java.security.cert.X509Certificate
+import java.time.LocalDate
+
+/** Test for Peer Device attestation verifier. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AttestationVerificationPeerDeviceVerifierTest {
+ private val certificateFactory = CertificateFactory.getInstance("X.509")
+ @Mock private lateinit var context: Context
+ private lateinit var trustAnchors: HashSet<TrustAnchor>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts()
+ trustAnchors = HashSet<TrustAnchor>()
+ rootCerts.forEach {
+ trustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypeChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypePublicKey() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+
+ val leafCert =
+ (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
+ as X509Certificate
+ val pkRequirements = Bundle()
+ pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded)
+
+ val result = verifier.verifyAttestation(
+ TYPE_PUBLIC_KEY, pkRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2023, 3, 1),
+ LocalDate.of(2023, 2, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, HashSet(), false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorMismatch() {
+ val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts()
+ val badTrustAnchors = HashSet<TrustAnchor>()
+ badTrustAnchorsCerts.forEach {
+ badTrustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ fun verifyAttestation_returnsFailureChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun String.fromPEMFileToCerts(): Collection<Certificate> {
+ return certificateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = this.fromPEMFileToCerts()
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
+ private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
+ "test_attestation_with_root_certs.pem"
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index c89e6a44ab6c..48b8779db112 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -97,6 +97,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
transitions {
taplInstrumentation.launchedAppState.quickSwitchToPreviousApp()
wmHelper.waitForFullScreenApp(testApp1.component)
+ wmHelper.waitSnapshotGone()
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForNavBarStatusBarVisible()
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 5d172e2c1b14..d6c8f4601437 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -51,7 +51,7 @@ import org.junit.runners.Parameterized
/**
* Test quick switching back to previous app from last opened app
*
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest`
*
* Actions:
* Launch an app [testApp1]
@@ -101,6 +101,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
taplInstrumentation.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
wmHelper.waitForFullScreenApp(testApp2.component)
+ wmHelper.waitSnapshotGone()
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForNavBarStatusBarVisible()
}
diff --git a/tests/SurfaceViewSyncTest/Android.bp b/tests/SurfaceViewSyncTest/Android.bp
new file mode 100644
index 000000000000..1c6e380aad63
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SurfaceViewSyncTest",
+ srcs: ["**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/SurfaceViewSyncTest/AndroidManifest.xml b/tests/SurfaceViewSyncTest/AndroidManifest.xml
new file mode 100644
index 000000000000..d085f8c168d3
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test">
+ <application>
+ <activity android:name="SurfaceViewSyncActivity"
+ android:label="SurfaceView Sync Test"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/SurfaceViewSyncTest/OWNERS b/tests/SurfaceViewSyncTest/OWNERS
new file mode 100644
index 000000000000..0862c05e0ee4
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
new file mode 100644
index 000000000000..4433b2148384
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/darker_gray"
+ tools:context="com.example.mysurfaceview.MainActivity">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="600dp" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:text="COLLAPSE SV"
+ android:id="@+id/expand_sv"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Switch
+ android:id="@+id/enable_sync_switch"
+ android:text="Enable Sync"
+ android:checked="true"
+ android:layout_alignParentEnd="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </RelativeLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
new file mode 100644
index 000000000000..06accec09686
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.window.SurfaceSyncer;
+
+/**
+ * Test app that allows the user to resize the SurfaceView and have the new buffer sync with the
+ * main window. This tests that {@link SurfaceSyncer} is working correctly.
+ */
+public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.Callback {
+ private static final String TAG = "SurfaceViewSyncActivity";
+
+ private SurfaceView mSurfaceView;
+ private boolean mLastExpanded = true;
+
+ private RenderingThread mRenderingThread;
+
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+
+ private Button mExpandButton;
+ private Switch mEnableSyncSwitch;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_surfaceview_sync);
+ mSurfaceView = findViewById(R.id.surface_view);
+ mSurfaceView.getHolder().addCallback(this);
+
+ WindowManager windowManager = getWindowManager();
+ WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
+ Rect bounds = metrics.getBounds();
+
+ LinearLayout container = findViewById(R.id.container);
+ mExpandButton = findViewById(R.id.expand_sv);
+ mEnableSyncSwitch = findViewById(R.id.enable_sync_switch);
+ mExpandButton.setOnClickListener(view -> updateSurfaceViewSize(bounds, container));
+
+ mRenderingThread = new RenderingThread(mSurfaceView.getHolder());
+ }
+
+ private void updateSurfaceViewSize(Rect bounds, View container) {
+ final float height;
+ if (mLastExpanded) {
+ height = bounds.height() / 2f;
+ mExpandButton.setText("EXPAND SV");
+ } else {
+ height = bounds.height() / 1.5f;
+ mExpandButton.setText("COLLAPSE SV");
+ }
+ mLastExpanded = !mLastExpanded;
+
+ if (mEnableSyncSwitch.isChecked()) {
+ int syncId = mSurfaceSyncer.setupSync(() -> { });
+ mSurfaceSyncer.addToSync(syncId, mSurfaceView, frameCallback ->
+ mRenderingThread.setFrameCallback(frameCallback));
+ mSurfaceSyncer.addToSync(syncId, container);
+ mSurfaceSyncer.markSyncReady(syncId);
+ } else {
+ mRenderingThread.renderSlow();
+ }
+
+ ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
+ svParams.height = (int) height;
+ mSurfaceView.setLayoutParams(svParams);
+ }
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ final Canvas canvas = holder.lockCanvas();
+ canvas.drawARGB(255, 100, 100, 100);
+ holder.unlockCanvasAndPost(canvas);
+ mRenderingThread.startRendering();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mRenderingThread.stopRendering();
+ }
+
+ private static class RenderingThread extends HandlerThread {
+ private final SurfaceHolder mSurfaceHolder;
+ private Handler mHandler;
+ private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private boolean mRenderSlow;
+
+ int mColorValue = 0;
+ int mColorDelta = 10;
+
+ RenderingThread(SurfaceHolder holder) {
+ super("RenderingThread");
+ mSurfaceHolder = holder;
+ }
+
+ public void setFrameCallback(SurfaceSyncer.SurfaceViewFrameCallback frameCallback) {
+ if (mHandler != null) {
+ mHandler.post(() -> {
+ mFrameCallback = frameCallback;
+ mRenderSlow = true;
+ });
+ }
+ }
+
+ public void renderSlow() {
+ if (mHandler != null) {
+ mHandler.post(() -> mRenderSlow = true);
+ }
+ }
+
+ private final Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mFrameCallback != null) {
+ mFrameCallback.onFrameStarted();
+ }
+
+ if (mRenderSlow) {
+ try {
+ // Long delay from start to finish to mimic slow draw
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ mRenderSlow = false;
+ }
+
+ mColorValue += mColorDelta;
+ if (mColorValue > 245 || mColorValue < 10) {
+ mColorDelta *= -1;
+ }
+
+ Canvas c = mSurfaceHolder.lockCanvas();
+ c.drawRGB(255, mColorValue, 255 - mColorValue);
+ mSurfaceHolder.unlockCanvasAndPost(c);
+
+ if (mFrameCallback != null) {
+ mFrameCallback.onFrameComplete();
+ }
+ mFrameCallback = null;
+
+ mHandler.postDelayed(this, 50);
+ }
+ };
+
+ public void startRendering() {
+ start();
+ mHandler = new Handler(getLooper());
+ mHandler.post(mRunnable);
+ }
+
+ public void stopRendering() {
+ if (mHandler != null) {
+ mHandler.post(() -> mHandler.removeCallbacks(mRunnable));
+ }
+ }
+ }
+}
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
new file mode 100644
index 000000000000..c9c6c5cf193b
--- /dev/null
+++ b/tests/TrustTests/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "TrustTests",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml
new file mode 100644
index 000000000000..c94152da2bf6
--- /dev/null
+++ b/tests/TrustTests/AndroidManifest.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.trust.test"
+ android:targetSandboxVersion="2">
+
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
+ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.trust.TrustTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name=".UserUnlockRequestTrustAgent"
+ android:exported="true"
+ android:label="Test Agent"
+ android:permission="android.permission.BIND_TRUST_AGENT">
+ <intent-filter>
+ <action android:name="android.service.trust.TrustAgentService" />
+ </intent-filter>
+ </service>
+
+ <service
+ android:name=".LockUserTrustAgent"
+ android:exported="true"
+ android:label="Test Agent"
+ android:permission="android.permission.BIND_TRUST_AGENT">
+ <intent-filter>
+ <action android:name="android.service.trust.TrustAgentService" />
+ </intent-filter>
+ </service>
+
+ <service
+ android:name=".GrantAndRevokeTrustAgent"
+ android:exported="true"
+ android:label="Test Agent"
+ android:permission="android.permission.BIND_TRUST_AGENT">
+ <intent-filter>
+ <action android:name="android.service.trust.TrustAgentService" />
+ </intent-filter>
+ </service>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.trust.test">
+ </instrumentation>
+</manifest>
diff --git a/tests/TrustTests/AndroidTest.xml b/tests/TrustTests/AndroidTest.xml
new file mode 100644
index 000000000000..61b711eb7273
--- /dev/null
+++ b/tests/TrustTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="TrustTests configuration">
+ <option name="test-tag" value="TrustTests" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="TrustTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.trust.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/TrustTests/OWNERS b/tests/TrustTests/OWNERS
new file mode 100644
index 000000000000..e2c6ce15b51e
--- /dev/null
+++ b/tests/TrustTests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/trust/OWNERS
diff --git a/tests/TrustTests/README.md b/tests/TrustTests/README.md
new file mode 100644
index 000000000000..3427e30536d9
--- /dev/null
+++ b/tests/TrustTests/README.md
@@ -0,0 +1,40 @@
+# TrustTests framework tests
+
+These tests test the "trust" part of the platform primarily implemented via TrustManagerService in
+the system server and TrustAgentService in system apps.
+
+Tests are separated into separate files based on major groupings. When creating new tests, find a
+_closely_ matching existing test file or create a new test file. Prefer many test files over large
+test files.
+
+Each test file has its own trust agent. To create a new trust agent:
+
+1. Create a new class extending from `BaseTrustAgentService` class in your test file
+2. Add a new `<service>` stanza to `AndroidManifest.xml` in this directory for the new agent
+ following the pattern fo the existing agents.
+
+To run:
+
+```atest TrustTests```
+
+## Testing approach:
+
+1. Test the agent service as a black box; avoid inspecting internal state of the service or
+ modifying the system code outside of this directory.
+2. The primary interface to the system is through these three points:
+ 1. `TrustAgentService`, your agent created by the `TrustAgentRule` and accessible via
+ the `agent` property of the rule.
+ 1. Call command methods (e.g. `grantTrust`) directly on the agent
+ 2. Listen to events (e.g. `onUserRequestedUnlock`) by implementing the method in
+ your test's agent class and tracking invocations. See `UserUnlockRequestTest` for an
+ example.
+ 2. `TrustManager` which is the interface the rest of the system (e.g. SystemUI) has to the
+ service.
+ 1. Through this API, simulate system events that the service cares about
+ (e.g. `reportUnlockAttempt`).
+ 3. `TrustListener` which is the interface the rest of the system (e.g. SystemUI) uses to receive
+ events from the service.
+ 1. Through this, verify behavior that affects the rest of the system. For example,
+ see `LockStateTrackingRule`.
+3. To re-use code between tests, prefer creating new rules alongside the existing rules or adding
+ functionality to a _closely_ matching existing rule.
diff --git a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
new file mode 100644
index 000000000000..493f3bd22d2b
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.trust
+
+import android.service.trust.TrustAgentService
+import android.util.Log
+import kotlin.reflect.KClass
+
+/**
+ * Base class for test trust agents.
+ */
+abstract class BaseTrustAgentService : TrustAgentService() {
+
+ override fun onCreate() {
+ super.onCreate()
+ Log.d(TAG, "${this::class.simpleName} created")
+ instances[this::class] = this
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ instances.remove(this::class)
+ }
+
+ companion object {
+ private val instances =
+ mutableMapOf<KClass<out BaseTrustAgentService>, BaseTrustAgentService>()
+ private const val TAG = "BaseTrustAgentService"
+
+ fun instance(serviceClass: KClass<out BaseTrustAgentService>): BaseTrustAgentService? {
+ return instances[serviceClass]!!
+ }
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/TrustTestActivity.kt b/tests/TrustTests/src/android/trust/TrustTestActivity.kt
new file mode 100644
index 000000000000..6c56feace3d7
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/TrustTestActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.trust
+
+import android.app.Activity
+import android.os.Bundle
+
+/**
+ * Activity for testing Trust.
+ */
+class TrustTestActivity : Activity() {
+
+ public override fun onCreate(icicle: Bundle?) {
+ super.onCreate(icicle)
+ setTurnScreenOn(true)
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
new file mode 100644
index 000000000000..790afd389152
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.trust.test
+
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.LockStateTrackingRule
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TrustAgentRule
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for testing revokeTrust & grantTrust for non-renewable trust.
+ *
+ * atest TrustTests:GrantAndRevokeTrustTest
+ */
+@RunWith(AndroidJUnit4::class)
+class GrantAndRevokeTrustTest {
+ private val uiDevice = UiDevice.getInstance(getInstrumentation())
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<GrantAndRevokeTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Before
+ fun manageTrust() {
+ trustAgentRule.agent.setManagingTrust(true)
+ }
+
+ // This test serves a baseline for Grant tests, verifying that the default behavior of the
+ // device is to lock when put to sleep
+ @Test
+ fun sleepingDeviceWithoutGrantLocksDevice() {
+ uiDevice.sleep()
+ await()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantKeepsDeviceUnlocked() {
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0)
+ uiDevice.sleep()
+ await()
+
+ lockStateTrackingRule.assertUnlocked()
+ }
+
+ @Test
+ fun grantKeepsDeviceUnlocked_untilRevoked() {
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0)
+ await()
+ uiDevice.sleep()
+ trustAgentRule.agent.revokeTrust()
+ await()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ companion object {
+ private const val TAG = "GrantAndRevokeTrustTest"
+ private const val GRANT_MESSAGE = "granted by test"
+ private fun await() = Thread.sleep(250)
+ }
+}
+
+class GrantAndRevokeTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/LockUserTest.kt b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
new file mode 100644
index 000000000000..83fc28fee818
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.trust.test
+
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.LockStateTrackingRule
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TrustAgentRule
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for testing lockUser.
+ *
+ * atest TrustTests:LockUserTest
+ */
+@RunWith(AndroidJUnit4::class)
+class LockUserTest {
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<LockUserTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Ignore("Causes issues with subsequent tests") // TODO: Enable test
+ @Test
+ fun lockUser_locksTheDevice() {
+ Log.i(TAG, "Locking user")
+ trustAgentRule.agent.lockUser()
+ await()
+
+ assertThat(lockStateTrackingRule.lockState.locked).isTrue()
+ }
+
+ companion object {
+ private const val TAG = "LockUserTest"
+ private fun await() = Thread.sleep(250)
+ }
+}
+
+class LockUserTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
new file mode 100644
index 000000000000..f8783fbaf121
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.trust.test
+
+import android.app.trust.TrustManager
+import android.content.Context
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TrustAgentRule
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for testing the user unlock trigger.
+ *
+ * atest TrustTests:UserUnlockRequestTest
+ */
+@RunWith(AndroidJUnit4::class)
+class UserUnlockRequestTest {
+ private val context: Context = getApplicationContext()
+ private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ private val userId = context.userId
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val trustAgentRule = TrustAgentRule<UserUnlockRequestTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(trustAgentRule)
+
+ @Test
+ fun reportUserRequestedUnlock_propagatesToAgent() {
+ val oldCount = trustAgentRule.agent.onUserRequestedUnlockCallCount
+ trustManager.reportUserRequestedUnlock(userId)
+ await()
+
+ assertThat(trustAgentRule.agent.onUserRequestedUnlockCallCount)
+ .isEqualTo(oldCount + 1)
+ }
+
+ companion object {
+ private const val TAG = "UserUnlockRequestTest"
+ private fun await() = Thread.sleep(250)
+ }
+}
+
+class UserUnlockRequestTrustAgent : BaseTrustAgentService() {
+ var onUserRequestedUnlockCallCount: Long = 0
+ private set
+
+ override fun onUserRequestedUnlock() {
+ Log.i(TAG, "onUserRequestedUnlock")
+ onUserRequestedUnlockCallCount++
+ }
+
+ companion object {
+ private const val TAG = "UserUnlockRequestTrustAgent"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
new file mode 100644
index 000000000000..0023af8893e2
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.trust.test.lib
+
+import android.app.trust.TrustManager
+import android.app.trust.TrustManager.TrustListener
+import android.content.Context
+import android.util.Log
+import android.view.WindowManagerGlobal
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Rule for tracking the lock state of the device based on events emitted to [TrustListener].
+ */
+class LockStateTrackingRule : TestRule {
+ private val context: Context = getApplicationContext()
+ private val windowManager = WindowManagerGlobal.getWindowManagerService()
+
+ @Volatile lateinit var lockState: LockState
+ private set
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ lockState = LockState(locked = windowManager.isKeyguardLocked)
+ val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ val listener = Listener()
+
+ trustManager.registerTrustListener(listener)
+ try {
+ base.evaluate()
+ } finally {
+ trustManager.unregisterTrustListener(listener)
+ }
+ }
+ }
+
+ fun assertLocked() = assertThat(lockState.locked).isTrue()
+ fun assertUnlocked() = assertThat(lockState.locked).isFalse()
+
+ inner class Listener : TrustListener {
+ override fun onTrustChanged(
+ enabled: Boolean,
+ userId: Int,
+ flags: Int,
+ trustGrantedMessages: MutableList<String>
+ ) {
+ Log.d(TAG, "Device became trusted=$enabled")
+ lockState = lockState.copy(locked = !enabled)
+ }
+
+ override fun onTrustManagedChanged(enabled: Boolean, userId: Int) {
+ }
+
+ override fun onTrustError(message: CharSequence) {
+ }
+ }
+
+ data class LockState(
+ val locked: Boolean? = null
+ )
+
+ companion object {
+ private const val TAG = "LockStateTrackingRule"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
new file mode 100644
index 000000000000..c682a00eb8b9
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.trust.test.lib
+
+import android.content.Context
+import android.util.Log
+import android.view.WindowManagerGlobal
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Sets a screen lock on the device for the duration of the test.
+ */
+class ScreenLockRule : TestRule {
+ private val context: Context = getApplicationContext()
+ private val windowManager = WindowManagerGlobal.getWindowManagerService()
+ private val lockPatternUtils = LockPatternUtils(context)
+ private var instantLockSavedValue = false
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ verifyNoScreenLockAlreadySet()
+ verifyKeyguardDismissed()
+ setScreenLock()
+ setLockOnPowerButton()
+
+ try {
+ base.evaluate()
+ } finally {
+ removeScreenLock()
+ revertLockOnPowerButton()
+ }
+ }
+ }
+
+ private fun verifyNoScreenLockAlreadySet() {
+ assertWithMessage("Screen Lock must not already be set on device")
+ .that(lockPatternUtils.isSecure(context.userId))
+ .isFalse()
+ }
+
+ private fun verifyKeyguardDismissed() {
+ windowManager.dismissKeyguard(null, null)
+ Thread.sleep(250)
+ assertWithMessage("Keyguard should be unlocked")
+ .that(windowManager.isKeyguardLocked)
+ .isFalse()
+ }
+
+ private fun setScreenLock() {
+ lockPatternUtils.setLockCredential(
+ LockscreenCredential.createPin(PIN),
+ LockscreenCredential.createNone(),
+ context.userId
+ )
+ assertWithMessage("Screen Lock should now be set")
+ .that(lockPatternUtils.isSecure(context.userId))
+ .isTrue()
+ Log.i(TAG, "Device PIN set to $PIN")
+ }
+
+ private fun setLockOnPowerButton() {
+ instantLockSavedValue = lockPatternUtils.getPowerButtonInstantlyLocks(context.userId)
+ lockPatternUtils.setPowerButtonInstantlyLocks(true, context.userId)
+ }
+
+ private fun removeScreenLock() {
+ lockPatternUtils.setLockCredential(
+ LockscreenCredential.createNone(),
+ LockscreenCredential.createPin(PIN),
+ context.userId
+ )
+ Log.i(TAG, "Device PIN cleared; waiting 50 ms then dismissing Keyguard")
+ Thread.sleep(50)
+ windowManager.dismissKeyguard(null, null)
+ }
+
+ private fun revertLockOnPowerButton() {
+ lockPatternUtils.setPowerButtonInstantlyLocks(instantLockSavedValue, context.userId)
+ }
+
+ companion object {
+ private const val TAG = "ScreenLockRule"
+ private const val PIN = "0000"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
new file mode 100644
index 000000000000..2a9e00276475
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.trust.test.lib
+
+import android.app.trust.TrustManager
+import android.content.ComponentName
+import android.content.Context
+import android.trust.BaseTrustAgentService
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.android.internal.widget.LockPatternUtils
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import kotlin.reflect.KClass
+
+/**
+ * Enables a trust agent and causes the system service to bind to it.
+ *
+ * The enabled agent can be accessed during the test via the [agent] property.
+ *
+ * @constructor Creates the rule. Do not use; instead, use [invoke].
+ */
+class TrustAgentRule<T : BaseTrustAgentService>(
+ private val serviceClass: KClass<T>
+) : TestRule {
+ private val context: Context = getApplicationContext()
+ private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ private val lockPatternUtils = LockPatternUtils(context)
+
+ val agent get() = BaseTrustAgentService.instance(serviceClass) as T
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ verifyTrustServiceRunning()
+ unlockDeviceWithCredential()
+ enableTrustAgent()
+ waitForEnablement()
+
+ try {
+ verifyAgentIsRunning()
+ base.evaluate()
+ } finally {
+ disableTrustAgent()
+ }
+ }
+ }
+
+ private fun verifyTrustServiceRunning() {
+ assertWithMessage("Trust service is not running").that(trustManager).isNotNull()
+ }
+
+ private fun unlockDeviceWithCredential() {
+ Log.d(TAG, "Unlocking device with credential")
+ trustManager.reportUnlockAttempt(true, context.userId)
+ }
+
+ private fun enableTrustAgent() {
+ val componentName = ComponentName(context, serviceClass.java)
+ val userId = context.userId
+ Log.i(TAG, "Enabling trust agent ${componentName.flattenToString()} for user $userId")
+ val agents = mutableListOf(componentName)
+ .plus(lockPatternUtils.getEnabledTrustAgents(userId))
+ .distinct()
+ lockPatternUtils.setEnabledTrustAgents(agents, userId)
+ }
+
+ private fun waitForEnablement() {
+ Log.d(TAG, "Waiting for $WAIT_TIME ms")
+ Thread.sleep(WAIT_TIME)
+ Log.d(TAG, "Done waiting")
+ }
+
+ private fun verifyAgentIsRunning() {
+ assertWithMessage("${serviceClass.simpleName} should be running")
+ .that(BaseTrustAgentService.instance(serviceClass)).isNotNull()
+ }
+
+ private fun disableTrustAgent() {
+ val componentName = ComponentName(context, serviceClass.java)
+ val userId = context.userId
+ Log.i(TAG, "Disabling trust agent ${componentName.flattenToString()} for user $userId")
+ val agents = lockPatternUtils.getEnabledTrustAgents(userId).toMutableList()
+ .distinct()
+ .minus(componentName)
+ lockPatternUtils.setEnabledTrustAgents(agents, userId)
+ }
+
+ companion object {
+ /**
+ * Creates a new rule for the specified agent class. Example usage:
+ * ```
+ * @get:Rule val rule = TrustAgentRule<MyTestAgent>()
+ * ```
+ */
+ inline operator fun <reified T : BaseTrustAgentService> invoke() =
+ TrustAgentRule(T::class)
+
+ private const val TAG = "TrustAgentRule"
+ private val WAIT_TIME = 1000L
+ }
+}