summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java85
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java49
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java14
-rw-r--r--core/api/current.txt85
-rw-r--r--core/api/system-current.txt43
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java54
-rw-r--r--core/java/android/app/ActivityManager.java12
-rw-r--r--core/java/android/app/ActivityThread.java25
-rw-r--r--core/java/android/app/AppOpsManager.java278
-rw-r--r--core/java/android/app/ApplicationPackageManager.java7
-rw-r--r--core/java/android/app/BroadcastOptions.java49
-rw-r--r--core/java/android/app/ContextImpl.java6
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/app/Notification.java14
-rw-r--r--core/java/android/app/NotificationManager.java2
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java14
-rw-r--r--core/java/android/companion/virtual/VirtualDevice.java5
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java78
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java20
-rw-r--r--core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl28
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorCallback.java75
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorConfig.java114
-rw-r--r--core/java/android/content/ContentProvider.java2
-rw-r--r--core/java/android/content/Context.java15
-rw-r--r--core/java/android/content/Intent.java5
-rw-r--r--core/java/android/content/IntentFilter.java33
-rw-r--r--core/java/android/content/pm/PackageManager.java207
-rw-r--r--core/java/android/content/pm/ServiceInfo.java44
-rw-r--r--core/java/android/content/pm/UserPackage.java64
-rw-r--r--core/java/android/content/res/FontScaleConverterFactory.java70
-rw-r--r--core/java/android/content/res/OWNERS2
-rw-r--r--core/java/android/credentials/CreateCredentialRequest.java2
-rw-r--r--core/java/android/credentials/ui/CancelUiRequest.java79
-rw-r--r--core/java/android/credentials/ui/IntentFactory.java22
-rw-r--r--core/java/android/hardware/SystemSensorManager.java15
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java27
-rw-r--r--core/java/android/hardware/display/DisplayManager.java80
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java645
-rw-r--r--core/java/android/hardware/face/FaceAuthenticateOptions.java15
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java15
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/os/CancellationSignalBeamer.java325
-rw-r--r--core/java/android/os/UserManager.java1
-rw-r--r--core/java/android/provider/Settings.java54
-rw-r--r--core/java/android/provider/Telephony.java62
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialOption.java4
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java5
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java13
-rw-r--r--core/java/android/util/NtpTrustedTime.java9
-rw-r--r--core/java/android/util/SparseArrayMap.java8
-rw-r--r--core/java/android/view/Display.java9
-rw-r--r--core/java/android/view/InsetsSource.java3
-rw-r--r--core/java/android/view/OWNERS1
-rw-r--r--core/java/android/view/ViewRootImpl.java4
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java12
-rw-r--r--core/java/android/view/autofill/AutofillManager.java3
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java45
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java102
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java24
-rw-r--r--core/java/android/widget/Editor.java39
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl3
-rw-r--r--core/java/android/window/SnapshotDrawerUtils.java24
-rw-r--r--core/java/android/window/StartingWindowInfo.java50
-rw-r--r--core/java/android/window/StartingWindowRemovalInfo.java18
-rw-r--r--core/java/android/window/TaskOrganizer.java10
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java5
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java35
-rw-r--r--core/java/com/android/internal/inputmethod/IImeTracker.aidl (renamed from core/java/com/android/internal/view/IImeTracker.aidl)30
-rw-r--r--core/java/com/android/internal/midi/EventScheduler.java53
-rw-r--r--core/java/com/android/internal/midi/MidiEventMultiScheduler.java128
-rw-r--r--core/java/com/android/internal/midi/MidiEventScheduler.java2
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java26
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/jni/android_hardware_SensorManager.cpp7
-rw-r--r--core/res/res/values/config.xml11
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java2
-rw-r--r--core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java44
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java2
-rw-r--r--core/tests/coretests/src/android/content/pm/UserPackageTest.java39
-rw-r--r--core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt54
-rw-r--r--core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java224
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java11
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java14
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java12
-rw-r--r--core/tests/coretests/src/android/widget/TextViewContextMenuTest.java73
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java244
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTest.java25
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--graphics/java/android/graphics/Mesh.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java506
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java688
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java165
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java150
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java57
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt)10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt19
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java106
-rw-r--r--libs/hwui/Android.bp3
-rw-r--r--libs/hwui/DisplayListOps.in3
-rw-r--r--libs/hwui/MemoryPolicy.h4
-rw-r--r--libs/hwui/Mesh.cpp102
-rw-r--r--libs/hwui/Mesh.h208
-rw-r--r--libs/hwui/RecordingCanvas.cpp38
-rw-r--r--libs/hwui/RecordingCanvas.h16
-rw-r--r--libs/hwui/SafeMath.h107
-rw-r--r--libs/hwui/SkiaCanvas.cpp15
-rw-r--r--libs/hwui/SkiaCanvas.h3
-rw-r--r--libs/hwui/hwui/Canvas.h14
-rw-r--r--libs/hwui/jni/GraphicsJNI.h20
-rw-r--r--libs/hwui/jni/Interpolator.cpp4
-rw-r--r--libs/hwui/jni/JvmErrorReporter.h5
-rw-r--r--libs/hwui/jni/Mesh.cpp227
-rw-r--r--libs/hwui/jni/Mesh.h265
-rw-r--r--libs/hwui/jni/Path.cpp4
-rw-r--r--libs/hwui/jni/PathEffect.cpp6
-rw-r--r--libs/hwui/jni/Shader.cpp19
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp7
-rw-r--r--libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp5
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp59
-rw-r--r--libs/hwui/jni/android_graphics_Matrix.cpp2
-rw-r--r--libs/hwui/jni/android_graphics_Mesh.cpp350
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp8
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp5
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h1
-rw-r--r--media/java/android/media/AudioManager.java2
-rw-r--r--media/java/android/media/AudioRecord.java2
-rw-r--r--media/java/android/media/MediaDrm.java8
-rw-r--r--media/java/android/media/PlayerBase.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioRecordUnitTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioTrackUnitTest.java2
-rw-r--r--packages/CredentialManager/AndroidManifest.xml9
-rw-r--r--packages/CredentialManager/res/values/strings.xml2
-rw-r--r--packages/CredentialManager/res/values/themes.xml6
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt105
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt35
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt25
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt6
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt92
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt32
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt2
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt2
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt13
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt66
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt30
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt16
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt (renamed from core/java/android/window/IWindowlessStartingSurfaceCallback.aidl)24
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt291
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/HeadlineIcon.kt81
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt48
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt92
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt155
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt1052
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt5
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt517
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt8
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt17
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt36
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt64
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt38
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt56
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt48
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt98
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt75
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt143
-rw-r--r--packages/DynamicSystemInstallationService/AndroidManifest.xml7
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/OWNERS1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt5
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt9
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt17
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt30
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt8
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt96
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java8
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java42
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml49
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml9
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf_row.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java79
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt139
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java188
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt64
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java170
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt94
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt377
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java753
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java766
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt179
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt210
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java27
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ProxyManager.java62
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java80
-rw-r--r--services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java322
-rw-r--r--services/companion/java/com/android/server/companion/transport/RawTransport.java95
-rw-r--r--services/companion/java/com/android/server/companion/transport/SecureTransport.java134
-rw-r--r--services/companion/java/com/android/server/companion/transport/Transport.java181
-rw-r--r--services/companion/java/com/android/server/companion/virtual/SensorController.java70
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java352
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java152
-rw-r--r--services/core/java/com/android/server/BatteryService.java68
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java12
-rw-r--r--services/core/java/com/android/server/IntentResolver.java64
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java65
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java30
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerUtils.java23
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java16
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java116
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags6
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java15
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java364
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java19
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java78
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java37
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java39
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java3
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java4
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java2
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java41
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java15
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java15
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java1
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java1
-rw-r--r--services/core/java/com/android/server/display/OWNERS2
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java1
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java (renamed from services/core/java/com/android/server/display/DisplayModeDirector.java)68
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeTrackerService.java150
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java171
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodUtils.java9
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java41
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java4
-rw-r--r--services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java585
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteController.java17
-rw-r--r--services/core/java/com/android/server/media/DeviceRouteController.java193
-rw-r--r--services/core/java/com/android/server/media/LegacyBluetoothRouteController.java8
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java120
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java4
-rw-r--r--services/core/java/com/android/server/pm/Computer.java4
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java66
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java3
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/NoFilteringResolver.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java8
-rw-r--r--services/core/java/com/android/server/pm/ResolveIntentHelper.java16
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java69
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java17
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolver.java326
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java16
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java32
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java36
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java43
-rw-r--r--services/core/java/com/android/server/rollback/OWNERS4
-rw-r--r--services/core/java/com/android/server/sensors/SensorManagerInternal.java28
-rw-r--r--services/core/java/com/android/server/sensors/SensorService.java7
-rw-r--r--services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java18
-rw-r--r--services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java14
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java96
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java40
-rw-r--r--services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java105
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java178
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java29
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java13
-rw-r--r--services/core/java/com/android/server/wm/Session.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java54
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java7
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp232
-rw-r--r--services/core/jni/com_android_server_sensor_SensorService.cpp69
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java37
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java60
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java2
-rw-r--r--services/tests/InputMethodSystemServerTests/Android.bp4
-rw-r--r--services/tests/InputMethodSystemServerTests/AndroidTest.xml1
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java159
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java4
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java39
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java34
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java66
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java87
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java187
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java246
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java (renamed from services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java)19
-rw-r--r--services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java371
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java152
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java47
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java8
-rw-r--r--services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java312
-rw-r--r--services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java142
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java13
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java8
-rw-r--r--telecomm/java/android/telecom/CallControl.java51
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java6
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallControl.aidl1
-rw-r--r--telephony/java/android/telephony/DomainSelectionService.java9
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java4
-rw-r--r--telephony/java/android/telephony/TransportSelectorCallback.java4
-rw-r--r--telephony/java/android/telephony/WwanSelectorCallback.java3
-rwxr-xr-xtelephony/java/android/telephony/ims/ImsCallSession.java3
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java58
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatellite.aidl7
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl6
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java18
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl4
-rw-r--r--telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl2
-rw-r--r--test-mock/api/current.txt1
-rw-r--r--tests/FlickerTests/Android.bp7
-rw-r--r--tests/FlickerTests/AndroidTest.xml4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt19
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt25
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt17
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt15
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt17
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt25
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/Android.bp1
-rw-r--r--tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java14
-rw-r--r--tests/MidiTests/Android.bp40
-rw-r--r--tests/MidiTests/AndroidManifest.xml30
-rw-r--r--tests/MidiTests/AndroidTest.xml30
-rw-r--r--tests/MidiTests/OWNERS1
-rw-r--r--tests/MidiTests/TEST_MAPPING7
-rw-r--r--tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java342
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt4
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt23
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt9
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt9
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt4
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt19
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt8
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt24
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java407
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java41
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java110
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java40
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl8
-rw-r--r--wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java215
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java40
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java40
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java51
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java248
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java23
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java42
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java66
-rw-r--r--wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java107
662 files changed, 17700 insertions, 10825 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index f6c18d62ff1e..7f02cb36dfc1 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -397,18 +397,11 @@ public class JobInfo implements Parcelable {
public static final int FLAG_EXPEDITED = 1 << 4;
/**
- * Whether it's a data transfer job or not.
- *
- * @hide
- */
- public static final int FLAG_DATA_TRANSFER = 1 << 5;
-
- /**
* Whether it's a user initiated job or not.
*
* @hide
*/
- public static final int FLAG_USER_INITIATED = 1 << 6;
+ public static final int FLAG_USER_INITIATED = 1 << 5;
/**
* @hide
@@ -738,13 +731,6 @@ public class JobInfo implements Parcelable {
}
/**
- * @see JobInfo.Builder#setDataTransfer(boolean)
- */
- public boolean isDataTransfer() {
- return (flags & FLAG_DATA_TRANSFER) != 0;
- }
-
- /**
* @see JobInfo.Builder#setUserInitiated(boolean)
*/
public boolean isUserInitiated() {
@@ -1850,39 +1836,6 @@ public class JobInfo implements Parcelable {
}
/**
- * Indicates that this job will be used to transfer data to or from a remote server. The
- * system could attempt to run a data transfer job longer than a regular job if the data
- * being transferred is potentially very large and can take a long time to complete.
- *
- * <p>
- * You must provide an estimate of the payload size via
- * {@link #setEstimatedNetworkBytes(long, long)} when scheduling the job or use
- * {@link JobService#updateEstimatedNetworkBytes(JobParameters, long, long)} or
- * {@link JobService#updateEstimatedNetworkBytes(JobParameters, JobWorkItem, long, long)}
- * shortly after the job starts.
- *
- * <p>
- * For user-initiated transfers that must be started immediately, call
- * {@link #setUserInitiated(boolean) setUserInitiated(true)}. Otherwise, the system may
- * defer the job to a more opportune time.
- *
- * <p>
- * If you want to perform more than one data transfer job, consider enqueuing multiple
- * {@link JobWorkItem JobWorkItems} along with {@link #setDataTransfer(boolean)}.
- *
- * @see JobInfo#isDataTransfer()
- */
- @NonNull
- public Builder setDataTransfer(boolean dataTransfer) {
- if (dataTransfer) {
- mFlags |= FLAG_DATA_TRANSFER;
- } else {
- mFlags &= (~FLAG_DATA_TRANSFER);
- }
- return this;
- }
-
- /**
* Indicates that this job is being scheduled to fulfill an explicit user request.
* As such, user-initiated jobs can only be scheduled when the app is in the foreground
* or in a state where launching an activity is allowed, as defined
@@ -1909,6 +1862,11 @@ public class JobInfo implements Parcelable {
* {@link SecurityException}.
*
* <p>
+ * In {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, user-initiated jobs can only
+ * be used for network data transfers. As such, they must specify a required network via
+ * {@link #setRequiredNetwork(NetworkRequest)} or {@link #setRequiredNetworkType(int)}.
+ *
+ * <p>
* These jobs will not be subject to quotas and will be started immediately once scheduled
* if all constraints are met and the device system health allows for additional tasks.
*
@@ -2179,10 +2137,6 @@ public class JobInfo implements Parcelable {
if (isPeriodic) {
throw new IllegalArgumentException("An expedited job cannot be periodic");
}
- if ((flags & FLAG_DATA_TRANSFER) != 0) {
- throw new IllegalArgumentException(
- "An expedited job cannot also be a data transfer job");
- }
if (isUserInitiated) {
throw new IllegalArgumentException("An expedited job cannot be user-initiated");
}
@@ -2202,24 +2156,6 @@ public class JobInfo implements Parcelable {
}
}
- if ((flags & FLAG_DATA_TRANSFER) != 0) {
- if (backoffPolicy == BACKOFF_POLICY_LINEAR) {
- throw new IllegalArgumentException(
- "A data transfer job cannot have a linear backoff policy.");
- }
- if (hasLateConstraint) {
- throw new IllegalArgumentException("A data transfer job cannot have a deadline");
- }
- if ((flags & FLAG_PREFETCH) != 0) {
- throw new IllegalArgumentException(
- "A data transfer job cannot also be a prefetch job");
- }
- if (networkRequest == null) {
- throw new IllegalArgumentException(
- "A data transfer job must specify a valid network type");
- }
- }
-
if (isUserInitiated) {
if (hasEarlyConstraint) {
throw new IllegalArgumentException("A user-initiated job cannot have a time delay");
@@ -2245,6 +2181,15 @@ public class JobInfo implements Parcelable {
throw new IllegalArgumentException(
"Can't call addTriggerContentUri() on a user-initiated job");
}
+ // UIDTs
+ if (networkRequest == null) {
+ throw new IllegalArgumentException(
+ "A user-initaited data transfer job must specify a valid network type");
+ }
+ if (backoffPolicy == BACKOFF_POLICY_LINEAR) {
+ throw new IllegalArgumentException(
+ "A user-initiated data transfer job cannot have a linear backoff policy.");
+ }
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index bf3789fa0095..0af191af2a21 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -478,8 +478,6 @@ public class JobSchedulerService extends com.android.server.SystemService
case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
- case Constants.KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS:
- case Constants.KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS:
case Constants.KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS:
case Constants.KEY_RUNTIME_USER_INITIATED_LIMIT_MS:
case Constants.KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR:
@@ -574,10 +572,6 @@ public class JobSchedulerService extends com.android.server.SystemService
"runtime_free_quota_max_limit_ms";
private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
- private static final String KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
- "runtime_min_data_transfer_guarantee_ms";
- private static final String KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS =
- "runtime_data_transfer_limit_ms";
private static final String KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
"runtime_min_user_initiated_guarantee_ms";
private static final String KEY_RUNTIME_USER_INITIATED_LIMIT_MS =
@@ -616,10 +610,6 @@ public class JobSchedulerService extends com.android.server.SystemService
public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
@VisibleForTesting
public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
- public static final long DEFAULT_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
- DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
- public static final long DEFAULT_RUNTIME_DATA_TRANSFER_LIMIT_MS =
- DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
public static final long DEFAULT_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS);
public static final long DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS =
@@ -739,18 +729,6 @@ public class JobSchedulerService extends com.android.server.SystemService
public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
/**
- * The minimum amount of time we try to guarantee normal data transfer jobs will run for.
- */
- public long RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
- DEFAULT_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS;
-
- /**
- * The maximum amount of time we will let a normal data transfer job run for. This will only
- * apply if there are no other limits that apply to the specific data transfer job.
- */
- public long RUNTIME_DATA_TRANSFER_LIMIT_MS = DEFAULT_RUNTIME_DATA_TRANSFER_LIMIT_MS;
-
- /**
* The minimum amount of time we try to guarantee normal user-initiated jobs will run for.
*/
public long RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
@@ -885,8 +863,6 @@ public class JobSchedulerService extends com.android.server.SystemService
KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS,
KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
- KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS,
KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
KEY_RUNTIME_USER_INITIATED_LIMIT_MS,
KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
@@ -904,17 +880,6 @@ public class JobSchedulerService extends com.android.server.SystemService
properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
// Make sure min runtime is at least as long as regular jobs.
- RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
- properties.getLong(
- KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- DEFAULT_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS));
- // Max limit should be at least the min guarantee AND the free quota.
- RUNTIME_DATA_TRANSFER_LIMIT_MS = Math.max(RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
- Math.max(RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- properties.getLong(
- KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS,
- DEFAULT_RUNTIME_DATA_TRANSFER_LIMIT_MS)));
- // Make sure min runtime is at least as long as regular jobs.
RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
properties.getLong(
KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
@@ -993,10 +958,6 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
.println();
- pw.print(KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS).println();
- pw.print(KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS,
- RUNTIME_DATA_TRANSFER_LIMIT_MS).println();
pw.print(KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS).println();
pw.print(KEY_RUNTIME_USER_INITIATED_LIMIT_MS,
@@ -3291,7 +3252,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (job.shouldTreatAsUserInitiatedJob()
&& checkRunUserInitiatedJobsPermission(
job.getSourceUid(), job.getSourcePackageName())) {
- if (job.getJob().isDataTransfer()) {
+ if (job.getJob().getRequiredNetwork() != null) { // UI+DT
final long estimatedTransferTimeMs =
mConnectivityController.getEstimatedTransferTimeMs(job);
if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) {
@@ -3308,9 +3269,6 @@ public class JobSchedulerService extends com.android.server.SystemService
));
}
return mConstants.RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS;
- } else if (job.getJob().isDataTransfer()) {
- // For now, don't increase a bg data transfer's minimum guarantee.
- return mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS;
} else if (job.shouldTreatAsExpeditedJob()) {
// Don't guarantee RESTRICTED jobs more than 5 minutes.
return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
@@ -3328,7 +3286,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean allowLongerJob = job.shouldTreatAsUserInitiatedJob()
&& checkRunUserInitiatedJobsPermission(
job.getSourceUid(), job.getSourcePackageName());
- if (job.getJob().isDataTransfer() && allowLongerJob) { // UI+DT
+ if (job.getJob().getRequiredNetwork() != null && allowLongerJob) { // UI+DT
return mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS;
}
if (allowLongerJob) { // UI with LRJ permission
@@ -3337,9 +3295,6 @@ public class JobSchedulerService extends com.android.server.SystemService
if (job.shouldTreatAsUserInitiatedJob()) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
- if (job.getJob().isDataTransfer()) {
- return mConstants.RUNTIME_DATA_TRANSFER_LIMIT_MS;
- }
return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mConstants.USE_TARE_POLICY
? mTareController.getMaxJobExecutionTimeMsLocked(job)
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 29ab45533667..e60ed4ade9b7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -727,8 +727,11 @@ public final class JobServiceContext implements ServiceConnection {
// Exception-throwing-can down the road to JobParameters.completeWork >:(
return true;
}
- mService.mJobs.touchJob(mRunningJob);
- return mRunningJob.completeWorkLocked(workId);
+ if (mRunningJob.completeWorkLocked(workId)) {
+ mService.mJobs.touchJob(mRunningJob);
+ return true;
+ }
+ return false;
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1971a11ca98a..537a67039a82 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -828,6 +828,10 @@ public final class JobStatus {
}
}
+ /**
+ * Returns {@code true} if the JobWorkItem queue was updated,
+ * and {@code false} if nothing changed.
+ */
public boolean completeWorkLocked(int workId) {
if (executingWork != null) {
final int N = executingWork.size();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
index 9ada8dc3ef32..47b7e13446b8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -24,6 +24,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -50,7 +51,7 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
public static final String ACTION_UNFORCE_IDLE = "com.android.server.jobscheduler.UNFORCE_IDLE";
// After construction, mutations of idle/screen-on state will only happen
- // on the main looper thread, either in onReceive() or in an alarm callback.
+ // on the JobScheduler thread, either in onReceive() or in an alarm callback.
private boolean mIdle;
private boolean mGarageModeOn;
private boolean mForced;
@@ -90,7 +91,7 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
filter.addAction(ACTION_UNFORCE_IDLE);
filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
- context.registerReceiver(this, filter);
+ context.registerReceiver(this, filter, null, JobSchedulerBackgroundThread.getHandler());
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
index 140cca679e14..15d67662130f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -31,6 +31,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -47,8 +48,8 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
private AlarmManager mAlarm;
private PowerManager mPowerManager;
- // After construction, mutations of idle/screen-on state will only happen
- // on the main looper thread, either in onReceive() or in an alarm callback.
+ // After construction, mutations of idle/screen-on/projection states will only happen
+ // on the JobScheduler thread, either in onReceive(), in an alarm callback, or in on.*Changed.
private long mInactivityIdleThreshold;
private long mIdleWindowSlop;
private boolean mIdle;
@@ -101,12 +102,10 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
filter.addAction(Intent.ACTION_DOCK_IDLE);
filter.addAction(Intent.ACTION_DOCK_ACTIVE);
- context.registerReceiver(this, filter);
+ context.registerReceiver(this, filter, null, JobSchedulerBackgroundThread.getHandler());
- // TODO(b/172579710): Move the callbacks off the main executor and on to
- // JobSchedulerBackgroundThread.getExecutor() once synchronization is fixed in this class.
context.getSystemService(UiModeManager.class).addOnProjectionStateChangedListener(
- UiModeManager.PROJECTION_TYPE_ALL, context.getMainExecutor(),
+ UiModeManager.PROJECTION_TYPE_ALL, JobSchedulerBackgroundThread.getExecutor(),
mOnProjectionStateChangedListener);
}
@@ -226,7 +225,8 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id
Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed + " when=" + when);
}
mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
+ when, mIdleWindowSlop, "JS idleness",
+ JobSchedulerBackgroundThread.getExecutor(), mIdleAlarmListener);
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9f3ceb3209de..2db3eae5fab6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6784,7 +6784,7 @@ package android.app {
method public boolean areNotificationsEnabled();
method public boolean areNotificationsPaused();
method public boolean canNotifyAsPackage(@NonNull String);
- method public boolean canSendFullScreenIntent();
+ method public boolean canUseFullScreenIntent();
method public void cancel(int);
method public void cancel(@Nullable String, int);
method public void cancelAll();
@@ -8733,7 +8733,6 @@ package android.app.job {
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
- method public boolean isDataTransfer();
method public boolean isExpedited();
method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
@@ -8770,7 +8769,6 @@ package android.app.job {
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int);
- method @NonNull public android.app.job.JobInfo.Builder setDataTransfer(boolean);
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method @NonNull public android.app.job.JobInfo.Builder setExpedited(boolean);
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
@@ -9576,8 +9574,6 @@ package android.companion.virtual {
public final class VirtualDeviceManager {
method @NonNull public java.util.List<android.companion.virtual.VirtualDevice> getVirtualDevices();
- field public static final int DEVICE_ID_DEFAULT = 0; // 0x0
- field public static final int DEVICE_ID_INVALID = -1; // 0xffffffff
}
}
@@ -10354,6 +10350,8 @@ package android.content {
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
field public static final String CREDENTIAL_SERVICE = "credential";
field public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+ field public static final int DEVICE_ID_DEFAULT = 0; // 0x0
+ field public static final int DEVICE_ID_INVALID = -1; // 0xffffffff
field public static final String DEVICE_LOCK_SERVICE = "device_lock";
field public static final String DEVICE_POLICY_SERVICE = "device_policy";
field public static final String DISPLAY_HASH_SERVICE = "display_hash";
@@ -12448,7 +12446,7 @@ package android.content.pm {
method @Nullable public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12458,7 +12456,7 @@ package android.content.pm {
method public abstract int getApplicationEnabledSetting(@NonNull String);
method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
@@ -12470,10 +12468,10 @@ package android.content.pm {
method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
method public void getGroupOfPlatformPermission(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.String>);
method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+ method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags);
method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+ method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method @NonNull public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String);
method @NonNull public abstract byte[] getInstantAppCookie();
@@ -12485,20 +12483,20 @@ package android.content.pm {
method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract String getNameForUid(int);
- method @Deprecated @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
+ method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method public abstract int[] getPackageGids(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public int[] getPackageGids(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.pm.PackageInstaller getPackageInstaller();
- method @Deprecated public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getPackageUid(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract String[] getPackagesForUid(int);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
+ method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
method @NonNull public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @NonNull public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12507,17 +12505,17 @@ package android.content.pm {
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+ method @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Nullable public android.os.Bundle getSuspendedPackageAppExtras();
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
@@ -12546,18 +12544,18 @@ package android.content.pm {
method public abstract boolean isSafeMode();
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
method @NonNull public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable java.util.List<android.content.Intent>, @NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@Nullable String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
@@ -12568,11 +12566,11 @@ package android.content.pm {
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
+ method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
- method @Deprecated @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
+ method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
- method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
+ method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
method @Nullable public android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
method public abstract void setApplicationCategoryHint(@NonNull String, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
@@ -19370,7 +19368,7 @@ package android.hardware.camera2.params {
public final class OutputConfiguration implements android.os.Parcelable {
ctor public OutputConfiguration(@NonNull android.view.Surface);
ctor public OutputConfiguration(int, @NonNull android.view.Surface);
- ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+ ctor public <T> OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
method public void addSensorPixelModeUsed(int);
method public void addSurface(@NonNull android.view.Surface);
method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
@@ -19529,9 +19527,9 @@ package android.hardware.display {
public final class DisplayManager {
method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int);
method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(String);
@@ -19586,6 +19584,30 @@ package android.hardware.display {
method public void onStopped();
}
+ public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDensityDpi();
+ method @NonNull public java.util.List<java.lang.String> getDisplayCategories();
+ method public int getFlags();
+ method public int getHeight();
+ method @NonNull public String getName();
+ method public float getRequestedRefreshRate();
+ method @Nullable public android.view.Surface getSurface();
+ method public int getWidth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.VirtualDisplayConfig> CREATOR;
+ }
+
+ public static final class VirtualDisplayConfig.Builder {
+ ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig build();
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setSurface(@Nullable android.view.Surface);
+ }
+
}
package android.hardware.fingerprint {
@@ -42066,6 +42088,7 @@ package android.telecom {
}
public final class CallControl {
+ method public void answer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
method public void disconnect(@NonNull android.telecom.DisconnectCause, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
method @NonNull public android.os.ParcelUuid getCallId();
method public void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 47d9ab6d4b8f..3307a4fab922 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -588,6 +588,7 @@ package android.app {
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
method @RequiresPermission(value="android.permission.WATCH_APPOPS", conditional=true) public void startWatchingNoted(@NonNull String[], @NonNull android.app.AppOpsManager.OnOpNotedListener);
+ method @RequiresPermission(value="android.permission.WATCH_APPOPS", conditional=true) public void startWatchingNoted(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpNotedListener);
method public void stopWatchingNoted(@NonNull android.app.AppOpsManager.OnOpNotedListener);
field public static final int HISTORY_FLAGS_ALL = 3; // 0x3
field public static final int HISTORY_FLAG_AGGREGATE = 1; // 0x1
@@ -3216,8 +3217,8 @@ package android.companion.virtual {
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull public android.content.Context createContext();
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull java.util.List<java.lang.String>, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -3346,10 +3347,15 @@ package android.companion.virtual.sensor {
public interface VirtualSensorCallback {
method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
+ method public default void onDirectChannelConfigured(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int);
+ method public default void onDirectChannelCreated(@IntRange(from=1) int, @NonNull android.os.SharedMemory);
+ method public default void onDirectChannelDestroyed(@IntRange(from=1) int);
}
public final class VirtualSensorConfig implements android.os.Parcelable {
method public int describeContents();
+ method public int getDirectChannelTypesSupported();
+ method public int getHighestDirectReportRateLevel();
method @NonNull public String getName();
method public int getType();
method @Nullable public String getVendor();
@@ -3360,6 +3366,8 @@ package android.companion.virtual.sensor {
public static final class VirtualSensorConfig.Builder {
ctor public VirtualSensorConfig.Builder(int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
}
@@ -3864,15 +3872,15 @@ package android.content.pm {
method @NonNull public boolean canUserUninstall(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_METADATA) public android.os.PersistableBundle getAppMetadata(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.pm.dex.ArtManager getArtManager();
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
method @Nullable @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public CharSequence getHarmfulAppWarning(@NonNull String);
method @Nullable public String getIncidentReportApproverPackageName();
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(@NonNull android.content.pm.PackageManager.PackageInfoFlags, int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract android.graphics.drawable.Drawable getInstantAppIcon(String);
method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent();
@@ -3886,13 +3894,13 @@ package android.content.pm {
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -10022,7 +10030,7 @@ package android.net.wifi.sharedconnectivity.app {
method public int describeContents();
method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
method public int getNetworkSource();
- method @NonNull public int[] getSecurityTypes();
+ method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes();
method @NonNull public String getSsid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.KnownNetwork> CREATOR;
@@ -10032,10 +10040,10 @@ package android.net.wifi.sharedconnectivity.app {
public static final class KnownNetwork.Builder {
ctor public KnownNetwork.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build();
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSecurityTypes(@NonNull int[]);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String);
}
@@ -10075,6 +10083,11 @@ package android.net.wifi.sharedconnectivity.app {
method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
method public boolean disconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method @Nullable public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus getKnownNetworkConnectionStatus();
+ method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork> getKnownNetworks();
+ method @Nullable public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState getSettingsState();
+ method @Nullable public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus getTetherNetworkConnectionStatus();
+ method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork> getTetherNetworks();
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
}
@@ -10099,7 +10112,7 @@ package android.net.wifi.sharedconnectivity.app {
method public long getDeviceId();
method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
method @Nullable public String getHotspotBssid();
- method @Nullable public int[] getHotspotSecurityTypes();
+ method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes();
method @Nullable public String getHotspotSsid();
method @NonNull public String getNetworkName();
method public int getNetworkType();
@@ -10113,11 +10126,11 @@ package android.net.wifi.sharedconnectivity.app {
public static final class TetherNetwork.Builder {
ctor public TetherNetwork.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder addHotspotSecurityType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork build();
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceId(long);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotBssid(@NonNull String);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSecurityTypes(@NonNull int[]);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSsid(@NonNull String);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkName(@NonNull String);
method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkType(int);
@@ -17277,9 +17290,9 @@ package android.view.accessibility {
public final class AccessibilityManager {
method public int getAccessibilityWindowId(@Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy);
+ method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy);
+ method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 640506dace91..43fa6e262a14 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -145,7 +145,8 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL
field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
- field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8
+ field @Deprecated public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8
+ field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8
field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
field public static final int PROCESS_STATE_TOP = 2; // 0x2
field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff
@@ -930,7 +931,7 @@ package android.content.pm {
method @Nullable public String getDefaultTextClassifierPackageName();
method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
- method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
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();
@@ -2180,6 +2181,7 @@ package android.os {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getBootUser();
+ method public int getDisplayIdAssignedToUser();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
@@ -3546,6 +3548,8 @@ package android.view.inputmethod {
public final class InputMethodInfo implements android.os.Parcelable {
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String);
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
+ field public static final int COMPONENT_NAME_MAX_LENGTH = 1000; // 0x3e8
+ field public static final int MAX_IMES_PER_PACKAGE = 20; // 0x14
}
public final class InputMethodManager {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 6422865c043a..3615435b7d75 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -198,6 +198,26 @@ import java.util.function.Consumer;
* possible for a node to contain outdated information because the window content may change at any
* time.
* </p>
+ * <h3>Drawing Accessibility Overlays</h3>
+ * <p>Accessibility services can draw overlays on top of existing screen contents.
+ * Accessibility overlays can be used to visually highlight items on the screen
+ * e.g. indicate the current item with accessibility focus.
+ * Overlays can also offer the user a way to interact with the service directly and quickly
+ * customize the service's behavior.</p>
+ * <p>Accessibility overlays can be attached to a particular window or to the display itself.
+ * Attaching an overlay to a window allows the overly to move, grow and shrink as the window does.
+ * The overlay will maintain the same relative position within the window bounds as the window
+ * moves. The overlay will also maintain the same relative position within the window bounds if
+ * the window is resized.
+ * To attach an overlay to a window, use {@link attachAccessibilityOverlayToWindow}.
+ * Attaching an overlay to the display means that the overlay is independent of the active
+ * windows on that display.
+ * To attach an overlay to a display, use {@link attachAccessibilityOverlayToDisplay}. </p>
+ * <p> When positioning an overlay that is attached to a window, the service must use window
+ * coordinates. In order to position an overlay on top of an existing UI element it is necessary
+ * to know the bounds of that element in window coordinates. To find the bounds in window
+ * coordinates of an element, find the corresponding {@link AccessibilityNodeInfo} as discussed
+ * above and call {@link AccessibilityNodeInfo#getBoundsInWindow}. </p>
* <h3>Notification strategy</h3>
* <p>
* All accessibility services are notified of all events they have requested, regardless of their
@@ -3421,22 +3441,28 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Attaches a {@link android.view.SurfaceControl} containing an accessibility
+ * <p>Attaches a {@link android.view.SurfaceControl} containing an accessibility
* overlay to the
* specified display. This type of overlay should be used for content that does
* not need to
* track the location and size of Views in the currently active app e.g. service
* configuration
- * or general service UI. To remove this overlay and free the associated
+ * or general service UI.</p>
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}.
+ * To embed the View into a {@link android.view.SurfaceControl}, create a
+ * {@link android.view.SurfaceControlViewHost} and attach the View using
+ * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by
+ * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p>
+ * <p>To remove this overlay and free the associated
* resources, use
- * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
- * If the specified overlay has already been attached to the specified display
+ * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p>
+ * <p>If the specified overlay has already been attached to the specified display
* this method does nothing.
* If the specified overlay has already been attached to a previous display this
* function will transfer the overlay to the new display.
* Services can attach multiple overlays. Use
* <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>.
- * to coordinate the order of the overlays on screen.
+ * to coordinate the order of the overlays on screen.</p>
*
* @param displayId the display to which the SurfaceControl should be attached.
* @param sc the SurfaceControl containing the overlay content
@@ -3456,20 +3482,24 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Attaches an accessibility overlay {@link android.view.SurfaceControl} to the
+ * <p>Attaches an accessibility overlay {@link android.view.SurfaceControl} to the
* specified
* window. This method should be used when you want the overlay to move and
- * resize as the parent
- * window moves and resizes. To remove this overlay and free the associated
- * resources, use
- * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
- * If the specified overlay has already been attached to the specified window
+ * resize as the parent window moves and resizes.</p>
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}.
+ * To embed the View into a {@link android.view.SurfaceControl}, create a
+ * {@link android.view.SurfaceControlViewHost} and attach the View using
+ * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by
+ * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p>
+ * <p>To remove this overlay and free the associated resources, use
+ * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p>
+ * <p>If the specified overlay has already been attached to the specified window
* this method does nothing.
* If the specified overlay has already been attached to a previous window this
* function will transfer the overlay to the new window.
* Services can attach multiple overlays. Use
* <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>.
- * to coordinate the order of the overlays on screen.
+ * to coordinate the order of the overlays on screen.</p>
*
* @param accessibilityWindowId The window id, from
* {@link AccessibilityWindowInfo#getId()}.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8b6d7cbaa449..ba36d937edc3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -751,9 +751,17 @@ public class ActivityManager {
@SystemApi
public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2;
- /** @hide Process can access network despite any power saving resrictions */
+ /** @hide Process can access network despite any power saving restrictions */
@TestApi
- public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3;
+ public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 1 << 3;
+ /**
+ * @hide
+ * @deprecated Use {@link #PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK} instead.
+ */
+ @TestApi
+ @Deprecated
+ public static final int PROCESS_CAPABILITY_NETWORK =
+ PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
/**
* Flag used to indicate whether an app is allowed to start a foreground service from the
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b50245d14d3b..50275abe249a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -36,6 +36,7 @@ import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -49,6 +50,7 @@ import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupAnnotations.OperationType;
+import android.app.compat.CompatChanges;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -206,6 +208,7 @@ import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.SafeZipPathValidatorCallback;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.DecorView;
import com.android.internal.util.ArrayUtils;
@@ -219,6 +222,7 @@ import dalvik.system.AppSpecializationHooks;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
import dalvik.system.VMRuntime;
+import dalvik.system.ZipPathValidator;
import libcore.io.ForwardingOs;
import libcore.io.IoUtils;
@@ -4621,7 +4625,7 @@ public final class ActivityThread extends ClientTransactionHandler
ActivityManager.getService());
if (!service.isUiContext()) { // WindowProviderService is a UI Context.
VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
- if (mLastReportedDeviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT
+ if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT
|| vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
service.updateDeviceId(mLastReportedDeviceId);
}
@@ -6149,7 +6153,7 @@ public final class ActivityThread extends ClientTransactionHandler
private void updateDeviceIdForNonUIContexts(int deviceId) {
// Invalid device id is treated as a no-op.
- if (deviceId == VirtualDeviceManager.DEVICE_ID_INVALID) {
+ if (deviceId == Context.DEVICE_ID_INVALID) {
return;
}
if (deviceId == mLastReportedDeviceId) {
@@ -6703,6 +6707,11 @@ public final class ActivityThread extends ClientTransactionHandler
// Let libcore handle any compat changes after installing the list of compat changes.
AppSpecializationHooks.handleCompatChangesBeforeBindingApplication();
+ // Initialize the zip path validator callback depending on the targetSdk.
+ // This has to be after AppCompatCallbacks#install() so that the Compat
+ // checks work accordingly.
+ initZipPathValidatorCallback();
+
mBoundApplication = data;
mConfigurationController.setConfiguration(data.config);
mConfigurationController.setCompatConfiguration(data.config);
@@ -7070,6 +7079,18 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ /**
+ * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip
+ * entry names.
+ * Otherwise: clear the callback to the default validation.
+ */
+ private void initZipPathValidatorCallback() {
+ if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) {
+ ZipPathValidator.setCallback(new SafeZipPathValidatorCallback());
+ } else {
+ ZipPathValidator.clearCallback();
+ }
+ }
private void handleSetContentCaptureOptionsCallback(String packageName) {
if (mContentCaptureOptionsCallback != null) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 40ed269f57fd..9bf9e0c8eec3 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -25,6 +25,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -1464,6 +1465,149 @@ public class AppOpsManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int _NUM_OP = 134;
+ /**
+ * All app ops represented as strings.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = { "OPSTR_" }, value = {
+ OPSTR_COARSE_LOCATION,
+ OPSTR_FINE_LOCATION,
+ OPSTR_MONITOR_LOCATION,
+ OPSTR_MONITOR_HIGH_POWER_LOCATION,
+ OPSTR_GET_USAGE_STATS,
+ OPSTR_ACTIVATE_VPN,
+ OPSTR_READ_CONTACTS,
+ OPSTR_WRITE_CONTACTS,
+ OPSTR_READ_CALL_LOG,
+ OPSTR_WRITE_CALL_LOG,
+ OPSTR_READ_CALENDAR,
+ OPSTR_WRITE_CALENDAR,
+ OPSTR_CALL_PHONE,
+ OPSTR_READ_SMS,
+ OPSTR_RECEIVE_SMS,
+ OPSTR_RECEIVE_MMS,
+ OPSTR_RECEIVE_WAP_PUSH,
+ OPSTR_SEND_SMS,
+ OPSTR_CAMERA,
+ OPSTR_RECORD_AUDIO,
+ OPSTR_READ_PHONE_STATE,
+ OPSTR_ADD_VOICEMAIL,
+ OPSTR_USE_SIP,
+ OPSTR_PROCESS_OUTGOING_CALLS,
+ OPSTR_USE_FINGERPRINT,
+ OPSTR_BODY_SENSORS,
+ OPSTR_READ_CELL_BROADCASTS,
+ OPSTR_MOCK_LOCATION,
+ OPSTR_READ_EXTERNAL_STORAGE,
+ OPSTR_WRITE_EXTERNAL_STORAGE,
+ OPSTR_SYSTEM_ALERT_WINDOW,
+ OPSTR_WRITE_SETTINGS,
+ OPSTR_GET_ACCOUNTS,
+ OPSTR_READ_PHONE_NUMBERS,
+ OPSTR_PICTURE_IN_PICTURE,
+ OPSTR_INSTANT_APP_START_FOREGROUND,
+ OPSTR_ANSWER_PHONE_CALLS,
+ OPSTR_ACCEPT_HANDOVER,
+ OPSTR_GPS,
+ OPSTR_VIBRATE,
+ OPSTR_WIFI_SCAN,
+ OPSTR_POST_NOTIFICATION,
+ OPSTR_NEIGHBORING_CELLS,
+ OPSTR_WRITE_SMS,
+ OPSTR_RECEIVE_EMERGENCY_BROADCAST,
+ OPSTR_READ_ICC_SMS,
+ OPSTR_WRITE_ICC_SMS,
+ OPSTR_ACCESS_NOTIFICATIONS,
+ OPSTR_PLAY_AUDIO,
+ OPSTR_READ_CLIPBOARD,
+ OPSTR_WRITE_CLIPBOARD,
+ OPSTR_TAKE_MEDIA_BUTTONS,
+ OPSTR_TAKE_AUDIO_FOCUS,
+ OPSTR_AUDIO_MASTER_VOLUME,
+ OPSTR_AUDIO_VOICE_VOLUME,
+ OPSTR_AUDIO_RING_VOLUME,
+ OPSTR_AUDIO_MEDIA_VOLUME,
+ OPSTR_AUDIO_ALARM_VOLUME,
+ OPSTR_AUDIO_NOTIFICATION_VOLUME,
+ OPSTR_AUDIO_BLUETOOTH_VOLUME,
+ OPSTR_WAKE_LOCK,
+ OPSTR_MUTE_MICROPHONE,
+ OPSTR_TOAST_WINDOW,
+ OPSTR_PROJECT_MEDIA,
+ OPSTR_WRITE_WALLPAPER,
+ OPSTR_ASSIST_STRUCTURE,
+ OPSTR_ASSIST_SCREENSHOT,
+ OPSTR_TURN_SCREEN_ON,
+ OPSTR_RUN_IN_BACKGROUND,
+ OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
+ OPSTR_REQUEST_INSTALL_PACKAGES,
+ OPSTR_RUN_ANY_IN_BACKGROUND,
+ OPSTR_CHANGE_WIFI_STATE,
+ OPSTR_REQUEST_DELETE_PACKAGES,
+ OPSTR_BIND_ACCESSIBILITY_SERVICE,
+ OPSTR_MANAGE_IPSEC_TUNNELS,
+ OPSTR_START_FOREGROUND,
+ OPSTR_BLUETOOTH_SCAN,
+ OPSTR_BLUETOOTH_CONNECT,
+ OPSTR_BLUETOOTH_ADVERTISE,
+ OPSTR_USE_BIOMETRIC,
+ OPSTR_ACTIVITY_RECOGNITION,
+ OPSTR_SMS_FINANCIAL_TRANSACTIONS,
+ OPSTR_READ_MEDIA_AUDIO,
+ OPSTR_WRITE_MEDIA_AUDIO,
+ OPSTR_READ_MEDIA_VIDEO,
+ OPSTR_WRITE_MEDIA_VIDEO,
+ OPSTR_READ_MEDIA_IMAGES,
+ OPSTR_WRITE_MEDIA_IMAGES,
+ OPSTR_LEGACY_STORAGE,
+ OPSTR_ACCESS_MEDIA_LOCATION,
+ OPSTR_ACCESS_ACCESSIBILITY,
+ OPSTR_READ_DEVICE_IDENTIFIERS,
+ OPSTR_QUERY_ALL_PACKAGES,
+ OPSTR_MANAGE_EXTERNAL_STORAGE,
+ OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+ OPSTR_INTERACT_ACROSS_PROFILES,
+ OPSTR_ACTIVATE_PLATFORM_VPN,
+ OPSTR_LOADER_USAGE_STATS,
+ OPSTR_MANAGE_ONGOING_CALLS,
+ OPSTR_NO_ISOLATED_STORAGE,
+ OPSTR_PHONE_CALL_MICROPHONE,
+ OPSTR_PHONE_CALL_CAMERA,
+ OPSTR_RECORD_AUDIO_HOTWORD,
+ OPSTR_MANAGE_CREDENTIALS,
+ OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
+ OPSTR_RECORD_AUDIO_OUTPUT,
+ OPSTR_SCHEDULE_EXACT_ALARM,
+ OPSTR_FINE_LOCATION_SOURCE,
+ OPSTR_COARSE_LOCATION_SOURCE,
+ OPSTR_MANAGE_MEDIA,
+ OPSTR_UWB_RANGING,
+ OPSTR_NEARBY_WIFI_DEVICES,
+ OPSTR_ACTIVITY_RECOGNITION_SOURCE,
+ OPSTR_RECORD_INCOMING_PHONE_AUDIO,
+ OPSTR_ESTABLISH_VPN_SERVICE,
+ OPSTR_ESTABLISH_VPN_MANAGER,
+ OPSTR_ACCESS_RESTRICTED_SETTINGS,
+ OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+ OPSTR_READ_MEDIA_VISUAL_USER_SELECTED,
+ OPSTR_READ_WRITE_HEALTH_DATA,
+ OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
+ OPSTR_RUN_USER_INITIATED_JOBS,
+ OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION,
+ OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
+ OPSTR_FOREGROUND_SERVICE_SPECIAL_USE,
+ OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
+ OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
+ OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
+ OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
+ OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
+ OPSTR_USE_FULL_SCREEN_INTENT,
+ })
+ public @interface AppOpString {}
+
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
/** Access to fine location information. */
@@ -7736,18 +7880,19 @@ public class AppOpsManager {
}
/**
- * Start watching for noted app ops. An app op may be immediate or long running.
- * Immediate ops are noted while long running ones are started and stopped. This
- * method allows registering a listener to be notified when an app op is noted. If
- * an op is being noted by any package you will get a callback. To change the
- * watched ops for a registered callback you need to unregister and register it again.
+ * Start watching for noted app ops.
*
- * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
- * you can watch changes only for your UID.
+ * <p> Similar to {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}, but
+ * without an executor parameter.
*
- * @param ops The ops to watch.
- * @param callback Where to report changes.
+ * <p> Note that the listener will be called on the main thread using
+ * {@link Context.getMainThread()}. To specify the execution thread, use
+ * {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}.
+ *
+ * @param ops the ops to watch
+ * @param listener listener to notify when an app op is noted
*
+ * @see #startWatchingNoted(String[], Executor, OnOpNotedListener)
* @see #stopWatchingNoted(OnOpNotedListener)
* @see #noteOp(String, int, String, String, String)
*
@@ -7755,40 +7900,111 @@ public class AppOpsManager {
*/
@SystemApi
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
- public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+ public void startWatchingNoted(@NonNull @AppOpString String[] ops,
+ @NonNull OnOpNotedListener listener) {
final int[] intOps = new int[ops.length];
for (int i = 0; i < ops.length; i++) {
intOps[i] = strOpToOp(ops[i]);
}
- startWatchingNoted(intOps, callback);
+ startWatchingNoted(intOps, listener);
}
/**
- * Start watching for noted app ops. An app op may be immediate or long running.
- * Immediate ops are noted while long running ones are started and stopped. This
- * method allows registering a listener to be notified when an app op is noted. If
- * an op is being noted by any package you will get a callback. To change the
- * watched ops for a registered callback you need to unregister and register it again.
+ * Start watching for noted app ops.
*
- * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
- * you can watch changes only for your UID.
+ * <p> An app op may be immediate or long-running. Immediate ops are noted while long-running
+ * ones are started and stopped.
*
- * This allows observing noted ops by their raw op codes instead of string op names.
+ * <p> This method allows registering a listener to be notified when an app op is noted. To
+ * change the watched ops for a registered callback you need to unregister and register it
+ * again.
*
- * @param ops The ops to watch.
- * @param callback Where to report changes.
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission you can
+ * watch changes only for your UID.
+ *
+ * @param ops the ops to watch
+ * @param executor the executor on which the listener will be notified
+ * @param listener listener to notify when an app op is noted
+ *
+ * @see #startWatchingNoted(String[], OnOpNotedListener)
+ * @see #stopWatchingNoted(OnOpNotedListener)
+ * @see #noteOp(String, int, String, String, String)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ public void startWatchingNoted(@NonNull @AppOpString String[] ops,
+ @CallbackExecutor @NonNull Executor executor, @NonNull OnOpNotedListener listener) {
+ final int[] intOps = new int[ops.length];
+ for (int i = 0; i < ops.length; i++) {
+ intOps[i] = strOpToOp(ops[i]);
+ }
+ startWatchingNoted(intOps, executor, listener);
+ }
+
+ /**
+ * Start watching for noted app ops.
+ *
+ * <p> Similar to {@link #startWatchingNoted(int[], Executor, OnOpNotedListener)}, but without
+ * an executor parameter.
+ *
+ * <p> This method is also similar to {@link #startWatchingNoted(String[], OnOpNotedListener)},
+ * but allows observing noted ops by their raw op codes instead of string op names.
+ *
+ * <p> Note that the listener will be called on the main thread using
+ * {@link Context.getMainThread()}. To specify the execution thread, use
+ * {@link {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}.
+ *
+ * @param ops the ops to watch
+ * @param listener listener to notify when an app op is noted
*
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
* @see #startWatchingStarted(int[], OnOpStartedListener)
* @see #startWatchingNoted(String[], OnOpNotedListener)
+ * @see #startWatchingNoted(int[], Executor, OnOpNotedListener)
*
* @hide
*/
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
- public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) {
+ public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener listener) {
+ startWatchingNoted(ops, mContext.getMainExecutor(), listener);
+ }
+
+ /**
+ * Start watching for noted app ops.
+ *
+ * <p> This method is similar to
+ * {@link #startWatchingNoted(String[], Executor, OnOpNotedListener)}, but allows observing
+ * noted ops by their raw op codes instead of string op names.
+ *
+ * <p> An app op may be immediate or long-running. Immediate ops are noted while long-running
+ * ones are started and stopped.
+ *
+ * <p> This method allows registering a listener to be notified when an app op is noted. To
+ * change the watched ops for a registered callback you need to unregister and register it
+ * again.
+ *
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission you
+ * can watch changes only for your UID.
+ *
+ * @param ops the ops to watch
+ * @param executor the executor on which the listener will be notified
+ * @param listener listener to notify when an app op is noted
+ *
+ * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+ * @see #startWatchingStarted(int[], OnOpStartedListener)
+ * @see #startWatchingNoted(int[], Executor, OnOpNotedListener)
+ * @see #startWatchingNoted(String[], OnOpNotedListener)
+ *
+ * @hide
+ */
+ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ public void startWatchingNoted(@NonNull int[] ops,
+ @CallbackExecutor @NonNull Executor executor, @NonNull OnOpNotedListener listener) {
IAppOpsNotedCallback cb;
synchronized (mNotedWatchers) {
- cb = mNotedWatchers.get(callback);
+ cb = mNotedWatchers.get(listener);
if (cb != null) {
return;
}
@@ -7796,13 +8012,21 @@ public class AppOpsManager {
@Override
public void opNoted(int op, int uid, String packageName, String attributionTag,
int flags, int mode) {
- if (sAppOpInfos[op].name != null) {
- callback.onOpNoted(sAppOpInfos[op].name, uid, packageName, attributionTag,
- flags, mode);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ if (sAppOpInfos[op].name != null) {
+ listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+ attributionTag,
+ flags, mode);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
};
- mNotedWatchers.put(callback, cb);
+ mNotedWatchers.put(listener, cb);
}
try {
mService.startWatchingNoted(ops, cb);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a99815c20fe6..6301ad7f1278 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -556,12 +556,7 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags)
throws NameNotFoundException {
- return getActivityInfoAsUser(className, flags, getUserId());
- }
-
- @Override
- public ActivityInfo getActivityInfoAsUser(ComponentName className,
- ComponentInfoFlags flags, @UserIdInt int userId) throws NameNotFoundException {
+ final int userId = getUserId();
try {
ActivityInfo ai = mPM.getActivityInfo(className,
updateFlagsForComponent(flags.getValue(), userId, null), userId);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index fe40a4ce3d13..f48181b95892 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -60,7 +60,6 @@ public class BroadcastOptions extends ComponentOptions {
private String[] mRequireNoneOfPermissions;
private long mRequireCompatChangeId = CHANGE_INVALID;
private long mIdForResponseEvent;
- private @Nullable IntentFilter mRemoveMatchingFilter;
private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
private @Nullable String mDeliveryGroupMatchingKey;
private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
@@ -190,12 +189,6 @@ public class BroadcastOptions extends ComponentOptions {
"android:broadcast.idForResponseEvent";
/**
- * Corresponds to {@link #setRemoveMatchingFilter}.
- */
- private static final String KEY_REMOVE_MATCHING_FILTER =
- "android:broadcast.removeMatchingFilter";
-
- /**
* Corresponds to {@link #setDeliveryGroupPolicy(int)}.
*/
private static final String KEY_DELIVERY_GROUP_POLICY =
@@ -274,18 +267,6 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
- * {@hide}
- * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead.
- */
- @Deprecated
- public static @NonNull BroadcastOptions makeRemovingMatchingFilter(
- @NonNull IntentFilter removeMatchingFilter) {
- BroadcastOptions opts = new BroadcastOptions();
- opts.setRemoveMatchingFilter(removeMatchingFilter);
- return opts;
- }
-
- /**
* Creates a new {@code BroadcastOptions} with no options initially set.
*/
public BroadcastOptions() {
@@ -315,8 +296,6 @@ public class BroadcastOptions extends ComponentOptions {
mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS);
mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
- mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER,
- IntentFilter.class);
mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY,
DELIVERY_GROUP_POLICY_ALL);
mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY);
@@ -797,31 +776,6 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
- * When enqueuing this broadcast, remove all pending broadcasts previously
- * sent by this app which match the given filter.
- * <p>
- * For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want
- * to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts.
- *
- * @hide
- * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead.
- */
- @Deprecated
- public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) {
- mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter);
- }
-
- /** @hide */
- public void clearRemoveMatchingFilter() {
- mRemoveMatchingFilter = null;
- }
-
- /** @hide */
- public @Nullable IntentFilter getRemoveMatchingFilter() {
- return mRemoveMatchingFilter;
- }
-
- /**
* Set delivery group policy for this broadcast to specify how multiple broadcasts belonging to
* the same delivery group has to be handled.
*
@@ -1092,9 +1046,6 @@ public class BroadcastOptions extends ComponentOptions {
if (mIdForResponseEvent != 0) {
b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
}
- if (mRemoveMatchingFilter != null) {
- b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter);
- }
if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) {
b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1c0be686330d..e3ec4937ba2c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -245,7 +245,7 @@ class ContextImpl extends Context {
@UnsupportedAppUsage
private @NonNull Resources mResources;
private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet.
- private int mDeviceId = VirtualDeviceManager.DEVICE_ID_DEFAULT;
+ private int mDeviceId = Context.DEVICE_ID_DEFAULT;
/**
* If set to {@code true} the resources for this context will be configured for mDisplay which
@@ -2812,7 +2812,7 @@ class ContextImpl extends Context {
@Override
public @NonNull Context createDeviceContext(int deviceId) {
- if (deviceId != VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+ if (deviceId != Context.DEVICE_ID_DEFAULT) {
VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
if (!vdm.isValidVirtualDeviceId(deviceId)) {
throw new IllegalArgumentException(
@@ -3092,7 +3092,7 @@ class ContextImpl extends Context {
@Override
public void updateDeviceId(int updatedDeviceId) {
- if (updatedDeviceId != VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+ if (updatedDeviceId != Context.DEVICE_ID_DEFAULT) {
VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
if (!vdm.isValidVirtualDeviceId(updatedDeviceId)) {
throw new IllegalArgumentException(
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f653e132f7e3..5c38c42fe21f 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -341,12 +341,6 @@ interface IActivityManager {
in String message, boolean force, int exceptionTypeId);
void crashApplicationWithTypeWithExtras(int uid, int initialPid, in String packageName,
int userId, in String message, boolean force, int exceptionTypeId, in Bundle extras);
- /** @deprecated -- use getProviderMimeTypeAsync */
- @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
- "Use {@link android.content.ContentResolver#getType} public API instead.")
- String getProviderMimeType(in Uri uri, int userId);
-
- oneway void getProviderMimeTypeAsync(in Uri uri, int userId, in RemoteCallback resultCallback);
oneway void getMimeTypeFilterAsync(in Uri uri, int userId, in RemoteCallback resultCallback);
// Cause the specified process to dump the specified heap.
boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ef5cd9334be7..440ee202cc5b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5758,7 +5758,7 @@ public class Notification implements Parcelable
List<Notification.Action> nonContextualActions = getNonContextualActions();
int numActions = Math.min(nonContextualActions.size(), MAX_ACTION_BUTTONS);
- boolean emphazisedMode = mN.fullScreenIntent != null
+ boolean emphasizedMode = mN.fullScreenIntent != null
|| p.mCallStyleActions
|| ((mN.flags & FLAG_FSI_REQUESTED_BUT_DENIED) != 0);
@@ -5771,7 +5771,7 @@ public class Notification implements Parcelable
big.setInt(R.id.actions, "setCollapsibleIndentDimen",
R.dimen.call_notification_collapsible_indent);
}
- big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
+ big.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
if (numActions > 0 && !p.mHideActions) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -5783,12 +5783,12 @@ public class Notification implements Parcelable
boolean actionHasValidInput = hasValidRemoteInput(action);
validRemoteInput |= actionHasValidInput;
- final RemoteViews button = generateActionButton(action, emphazisedMode, p);
- if (actionHasValidInput && !emphazisedMode) {
+ final RemoteViews button = generateActionButton(action, emphasizedMode, p);
+ if (actionHasValidInput && !emphasizedMode) {
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
}
- if (emphazisedMode && i > 0) {
+ if (emphasizedMode && i > 0) {
// Clear start margin from non-first buttons to reduce the gap between them.
// (8dp remaining gap is from all buttons' standard 4dp inset).
button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
@@ -7475,10 +7475,10 @@ public class Notification implements Parcelable
Resources resources = context.getResources();
boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
if (mPictureIcon != null) {
- int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
+ int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
? R.dimen.notification_big_picture_max_height_low_ram
: R.dimen.notification_big_picture_max_height);
- int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
+ int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
? R.dimen.notification_big_picture_max_width_low_ram
: R.dimen.notification_big_picture_max_width);
mPictureIcon.scaleDownIfNecessary(maxPictureWidth, maxPictureHeight);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 91efa755e4c9..d2f2c3c4e95a 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -873,7 +873,7 @@ public class NotificationManager {
* permission to your manifest, and use
* {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT}.
*/
- public boolean canSendFullScreenIntent() {
+ public boolean canUseFullScreenIntent() {
final int result = PermissionChecker.checkPermissionForPreflight(mContext,
android.Manifest.permission.USE_FULL_SCREEN_INTENT,
mContext.getAttributionSource());
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 50a7da1cede5..5ee10a50568d 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -115,6 +115,20 @@ public interface TimeDetector {
String SHELL_COMMAND_CONFIRM_TIME = "confirm_time";
/**
+ * A shell command that clears the network time signal used by {@link
+ * SystemClock#currentNetworkTimeClock()}.
+ * @hide
+ */
+ String SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME = "clear_system_clock_network_time";
+
+ /**
+ * A shell command that sets the network time signal used by {@link
+ * SystemClock#currentNetworkTimeClock()}.
+ * @hide
+ */
+ String SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME = "set_system_clock_network_time";
+
+ /**
* A shared utility method to create a {@link ManualTimeSuggestion}.
*
* @hide
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index f3f433540ef7..4a09186570e0 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -18,6 +18,7 @@ package android.companion.virtual;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,9 +39,9 @@ public final class VirtualDevice implements Parcelable {
* @hide
*/
public VirtualDevice(int id, @Nullable String name) {
- if (id <= VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+ if (id <= Context.DEVICE_ID_DEFAULT) {
throw new IllegalArgumentException("VirtualDevice ID mist be greater than "
- + VirtualDeviceManager.DEVICE_ID_DEFAULT);
+ + Context.DEVICE_ID_DEFAULT);
}
mId = id;
mName = name;
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index ae43c6eb8b85..6cc4c8a24c48 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -89,24 +89,6 @@ public final class VirtualDeviceManager {
private static final String TAG = "VirtualDeviceManager";
- private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
- | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
- | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
- | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
- | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
-
- /**
- * The default device ID, which is the ID of the primary (non-virtual) device.
- */
- public static final int DEVICE_ID_DEFAULT = 0;
-
- /**
- * Invalid device ID.
- */
- public static final int DEVICE_ID_INVALID = -1;
-
/**
* Broadcast Action: A Virtual Device was removed.
*
@@ -250,7 +232,7 @@ public final class VirtualDeviceManager {
public int getDeviceIdForDisplayId(int displayId) {
if (mService == null) {
Log.w(TAG, "Failed to retrieve virtual devices; no virtual device manager service.");
- return DEVICE_ID_DEFAULT;
+ return Context.DEVICE_ID_DEFAULT;
}
try {
return mService.getDeviceIdForDisplayId(displayId);
@@ -261,7 +243,7 @@ public final class VirtualDeviceManager {
/**
* Checks whether the passed {@code deviceId} is a valid virtual device ID or not.
- * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default
+ * {@link Context#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default
* device which is not a virtual device. {@code deviceId} must correspond to a virtual device
* created by {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}.
*
@@ -549,7 +531,11 @@ public final class VirtualDeviceManager {
* not create the virtual display.
*
* @see DisplayManager#createVirtualDisplay
+ *
+ * @deprecated use {@link #createVirtualDisplay(VirtualDisplayConfig, Executor,
+ * VirtualDisplay.Callback)}
*/
+ @Deprecated
@Nullable
public VirtualDisplay createVirtualDisplay(
@IntRange(from = 1) int width,
@@ -562,30 +548,16 @@ public final class VirtualDeviceManager {
VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
getVirtualDisplayName(), width, height, densityDpi)
.setSurface(surface)
- .setFlags(getVirtualDisplayFlags(flags))
+ .setFlags(flags)
.build();
- return createVirtualDisplayInternal(config, executor, callback);
+ return createVirtualDisplay(config, executor, callback);
}
/**
* Creates a virtual display for this virtual device. All displays created on the same
* device belongs to the same display group.
*
- * @param width The width of the virtual display in pixels, must be greater than 0.
- * @param height The height of the virtual display in pixels, must be greater than 0.
- * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
- * @param displayCategories The categories of the virtual display, indicating the type of
- * activities allowed to run on the display. Activities can declare their type using
- * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
- * @param surface The surface to which the content of the virtual display should
- * be rendered, or null if there is none initially. The surface can also be set later using
- * {@link VirtualDisplay#setSurface(Surface)}.
- * @param flags A combination of virtual display flags accepted by
- * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
- * automatically set for all virtual devices:
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+ * @param config The configuration of the display.
* @param executor The executor on which {@code callback} will be invoked. This is ignored
* if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
* not be null.
@@ -597,28 +569,6 @@ public final class VirtualDeviceManager {
*/
@Nullable
public VirtualDisplay createVirtualDisplay(
- @IntRange(from = 1) int width,
- @IntRange(from = 1) int height,
- @IntRange(from = 1) int densityDpi,
- @NonNull List<String> displayCategories,
- @Nullable Surface surface,
- @VirtualDisplayFlag int flags,
- @Nullable @CallbackExecutor Executor executor,
- @Nullable VirtualDisplay.Callback callback) {
- VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
- getVirtualDisplayName(), width, height, densityDpi)
- .setDisplayCategories(displayCategories)
- .setSurface(surface)
- .setFlags(getVirtualDisplayFlags(flags))
- .build();
- return createVirtualDisplayInternal(config, executor, callback);
- }
-
- /**
- * @hide
- */
- @Nullable
- private VirtualDisplay createVirtualDisplayInternal(
@NonNull VirtualDisplayConfig config,
@Nullable @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
@@ -907,16 +857,6 @@ public final class VirtualDeviceManager {
}
}
- /**
- * Returns the display flags that should be added to a particular virtual display.
- * Additional device-level flags from {@link
- * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
- * be added by DisplayManagerService.
- */
- private int getVirtualDisplayFlags(int flags) {
- return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags;
- }
-
private String getVirtualDisplayName() {
try {
// Currently this just use the device ID, which means all of the virtual displays
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index d8076b5c0fd7..9f3b60148004 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -35,6 +35,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SharedMemory;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -577,6 +578,25 @@ public final class VirtualDeviceParams implements Parcelable {
mExecutor.execute(() -> mCallback.onConfigurationChanged(
sensor, enabled, samplingPeriod, batchReportingLatency));
}
+
+ @Override
+ public void onDirectChannelCreated(int channelHandle,
+ @NonNull SharedMemory sharedMemory) {
+ mExecutor.execute(
+ () -> mCallback.onDirectChannelCreated(channelHandle, sharedMemory));
+ }
+
+ @Override
+ public void onDirectChannelDestroyed(int channelHandle) {
+ mExecutor.execute(() -> mCallback.onDirectChannelDestroyed(channelHandle));
+ }
+
+ @Override
+ public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor,
+ int rateLevel, int reportToken) {
+ mExecutor.execute(() -> mCallback.onDirectChannelConfigured(
+ channelHandle, sensor, rateLevel, reportToken));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index 7da9c3224400..3cb0572f3350 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -17,6 +17,7 @@
package android.companion.virtual.sensor;
import android.companion.virtual.sensor.VirtualSensor;
+import android.os.SharedMemory;
/**
* Interface for notifying the sensor owner about whether and how sensor events should be injected.
@@ -36,4 +37,31 @@ oneway interface IVirtualSensorCallback {
*/
void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
int batchReportLatencyMicros);
+
+ /**
+ * Called when a sensor direct channel is created.
+ *
+ * @param channelHandle Identifier of the channel that was created.
+ * @param sharedMemory The shared memory region for the direct sensor channel.
+ */
+ void onDirectChannelCreated(int channelHandle, in SharedMemory sharedMemory);
+
+ /**
+ * Called when a sensor direct channel is destroyed.
+ *
+ * @param channelHandle Identifier of the channel that was destroyed.
+ */
+ void onDirectChannelDestroyed(int channelHandle);
+
+ /**
+ * Called when a sensor direct channel is configured.
+ *
+ * @param channelHandle Identifier of the channel that was configured.
+ * @param sensor The sensor, for which the channel was configured.
+ * @param rateLevel The rate level used to configure the direct sensor channel.
+ * @param reportToken A positive sensor report token, used to differentiate between events from
+ * different sensors within the same channel.
+ */
+ void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel,
+ int reportToken);
}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
index e09718941302..f7af283a749b 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -17,8 +17,13 @@
package android.companion.virtual.sensor;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.hardware.Sensor;
+import android.hardware.SensorDirectChannel;
+import android.os.MemoryFile;
+import android.os.SharedMemory;
import java.time.Duration;
@@ -50,4 +55,74 @@ public interface VirtualSensorCallback {
*/
void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
@NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency);
+
+ /**
+ * Called when a {@link android.hardware.SensorDirectChannel} is created.
+ *
+ * <p>The {@link android.hardware.SensorManager} instance used to create the direct channel must
+ * be associated with the virtual device.
+ *
+ * <p>A typical order of callback invocations is:
+ * <ul>
+ * <li>{@code onDirectChannelCreated} - the channel handle and the associated shared memory
+ * should be stored by the virtual device</li>
+ * <li>{@code onDirectChannelConfigured} with a positive {@code rateLevel} - the virtual
+ * device should start writing to the shared memory for the associated channel with the
+ * requested parameters.</li>
+ * <li>{@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual
+ * device should stop writing to the shared memory for the associated channel.</li>
+ * <li>{@code onDirectChannelDestroyed} - the shared memory associated with the channel
+ * handle should be closed.</li>
+ * </ul>
+ *
+ * @param channelHandle Identifier of the newly created channel.
+ * @param sharedMemory writable shared memory region.
+ *
+ * @see android.hardware.SensorManager#createDirectChannel(MemoryFile)
+ * @see #onDirectChannelConfigured
+ * @see #onDirectChannelDestroyed
+ */
+ default void onDirectChannelCreated(@IntRange(from = 1) int channelHandle,
+ @NonNull SharedMemory sharedMemory) {}
+
+ /**
+ * Called when a {@link android.hardware.SensorDirectChannel} is destroyed.
+ *
+ * <p>The virtual device must perform any clean-up and close the shared memory that was
+ * received with the {@link #onDirectChannelCreated} callback and the corresponding
+ * {@code channelHandle}.
+ *
+ * @param channelHandle Identifier of the channel that was destroyed.
+ *
+ * @see SensorDirectChannel#close()
+ */
+ default void onDirectChannelDestroyed(@IntRange(from = 1) int channelHandle) {}
+
+ /**
+ * Called when a {@link android.hardware.SensorDirectChannel} is configured.
+ *
+ * <p>Sensor events for the corresponding sensor should be written at the indicated rate to the
+ * shared memory region that was received with the {@link #onDirectChannelCreated} callback and
+ * the corresponding {@code channelHandle}. The events should be written in the correct format
+ * and with the provided {@code reportToken} until the channel is reconfigured with
+ * {@link SensorDirectChannel#RATE_STOP}.
+ *
+ * <p>The sensor must support direct channel in order for this callback to be invoked. Only
+ * {@link MemoryFile} sensor direct channels are supported for virtual sensors.
+ *
+ * @param channelHandle Identifier of the channel that was configured.
+ * @param sensor The sensor, for which the channel was configured.
+ * @param rateLevel The rate level used to configure the direct sensor channel.
+ * @param reportToken A positive sensor report token, used to differentiate between events from
+ * different sensors within the same channel.
+ *
+ * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int)
+ * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int)
+ * @see android.hardware.SensorManager#createDirectChannel(MemoryFile)
+ * @see #onDirectChannelCreated
+ * @see SensorDirectChannel#configure(Sensor, int)
+ */
+ default void onDirectChannelConfigured(@IntRange(from = 1) int channelHandle,
+ @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel,
+ @IntRange(from = 1) int reportToken) {}
}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 6d45365ebbd4..ffbdff8c2e3b 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -21,11 +21,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.Sensor;
+import android.hardware.SensorDirectChannel;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
+
/**
* Configuration for creation of a virtual sensor.
* @see VirtualSensor
@@ -33,6 +35,14 @@ import java.util.Objects;
*/
@SystemApi
public final class VirtualSensorConfig implements Parcelable {
+ private static final String TAG = "VirtualSensorConfig";
+
+ // Mask for direct mode highest rate level, bit 7, 8, 9.
+ private static final int DIRECT_REPORT_MASK = 0x380;
+ private static final int DIRECT_REPORT_SHIFT = 7;
+
+ // Mask for supported direct channel, bit 10, 11
+ private static final int DIRECT_CHANNEL_SHIFT = 10;
private final int mType;
@NonNull
@@ -40,16 +50,21 @@ public final class VirtualSensorConfig implements Parcelable {
@Nullable
private final String mVendor;
- private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) {
+ private final int mFlags;
+
+ private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor,
+ int flags) {
mType = type;
mName = name;
mVendor = vendor;
+ mFlags = flags;
}
private VirtualSensorConfig(@NonNull Parcel parcel) {
mType = parcel.readInt();
mName = parcel.readString8();
mVendor = parcel.readString8();
+ mFlags = parcel.readInt();
}
@Override
@@ -62,6 +77,7 @@ public final class VirtualSensorConfig implements Parcelable {
parcel.writeInt(mType);
parcel.writeString8(mName);
parcel.writeString8(mVendor);
+ parcel.writeInt(mFlags);
}
/**
@@ -92,22 +108,64 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Returns the highest supported direct report mode rate level of the sensor.
+ *
+ * @see Sensor#getHighestDirectReportRateLevel()
+ */
+ @SensorDirectChannel.RateLevel
+ public int getHighestDirectReportRateLevel() {
+ int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
+ return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
+ ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+ }
+
+ /**
+ * Returns a combination of all supported direct channel types.
+ *
+ * @see Builder#setDirectChannelTypesSupported(int)
+ * @see Sensor#isDirectChannelTypeSupported(int)
+ */
+ public @SensorDirectChannel.MemoryType int getDirectChannelTypesSupported() {
+ int memoryTypes = 0;
+ if ((mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0) {
+ memoryTypes |= SensorDirectChannel.TYPE_MEMORY_FILE;
+ }
+ if ((mFlags & (1 << (DIRECT_CHANNEL_SHIFT + 1))) > 0) {
+ memoryTypes |= SensorDirectChannel.TYPE_HARDWARE_BUFFER;
+ }
+ return memoryTypes;
+ }
+
+ /**
+ * Returns the sensor flags.
+ * @hide
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* Builder for {@link VirtualSensorConfig}.
*/
public static final class Builder {
+ private static final int FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED =
+ 1 << DIRECT_CHANNEL_SHIFT;
private final int mType;
@NonNull
private final String mName;
@Nullable
private String mVendor;
+ private int mFlags;
+ @SensorDirectChannel.RateLevel
+ int mHighestDirectReportRateLevel;
/**
* Creates a new builder.
*
* @param type The type of the sensor, matching {@link Sensor#getType}.
* @param name The name of the sensor. Must be unique among all sensors with the same type
- * that belong to the same virtual device.
+ * that belong to the same virtual device.
*/
public Builder(int type, @NonNull String name) {
mType = type;
@@ -119,7 +177,19 @@ public final class VirtualSensorConfig implements Parcelable {
*/
@NonNull
public VirtualSensorConfig build() {
- return new VirtualSensorConfig(mType, mName, mVendor);
+ if (mHighestDirectReportRateLevel > 0) {
+ if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) == 0) {
+ throw new IllegalArgumentException("Setting direct channel type is required "
+ + "for sensors with direct channel support.");
+ }
+ mFlags |= mHighestDirectReportRateLevel << DIRECT_REPORT_SHIFT;
+ }
+ if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) > 0
+ && mHighestDirectReportRateLevel == 0) {
+ throw new IllegalArgumentException("Highest direct report rate level is "
+ + "required for sensors with direct channel support.");
+ }
+ return new VirtualSensorConfig(mType, mName, mVendor, mFlags);
}
/**
@@ -130,6 +200,44 @@ public final class VirtualSensorConfig implements Parcelable {
mVendor = vendor;
return this;
}
+
+ /**
+ * Sets the highest supported rate level for direct sensor report.
+ *
+ * @see VirtualSensorConfig#getHighestDirectReportRateLevel()
+ */
+ @NonNull
+ public VirtualSensorConfig.Builder setHighestDirectReportRateLevel(
+ @SensorDirectChannel.RateLevel int rateLevel) {
+ mHighestDirectReportRateLevel = rateLevel;
+ return this;
+ }
+
+ /**
+ * Sets whether direct sensor channel of the given types is supported.
+ *
+ * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags
+ * indicating the types of shared memory supported for creating direct channels. Only
+ * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual
+ * sensors.
+ * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is
+ * set to be supported.
+ */
+ @NonNull
+ public VirtualSensorConfig.Builder setDirectChannelTypesSupported(
+ @SensorDirectChannel.MemoryType int memoryTypes) {
+ if ((memoryTypes & SensorDirectChannel.TYPE_MEMORY_FILE) > 0) {
+ mFlags |= FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED;
+ } else {
+ mFlags &= ~FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED;
+ }
+ if ((memoryTypes & ~SensorDirectChannel.TYPE_MEMORY_FILE) > 0) {
+ throw new IllegalArgumentException(
+ "Only TYPE_MEMORY_FILE direct channels can be supported for virtual "
+ + "sensors.");
+ }
+ return this;
+ }
}
@NonNull
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 795c77ff5105..d3502c5254c8 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -389,6 +389,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
@Override
public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) {
+ uri = validateIncomingUri(uri);
+ uri = maybeGetUriWithoutUserId(uri);
final Bundle result = new Bundle();
try {
result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri));
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4dccc8d0e81a..658702f2b1cc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -135,6 +135,15 @@ public abstract class Context {
@VisibleForTesting
public static final long OVERRIDABLE_COMPONENT_CALLBACKS = 193247900L;
+ /**
+ * The default device ID, which is the ID of the primary (non-virtual) device.
+ */
+ public static final int DEVICE_ID_DEFAULT = 0;
+ /**
+ * Invalid device ID.
+ */
+ public static final int DEVICE_ID_INVALID = -1;
+
/** @hide */
@IntDef(flag = true, prefix = { "MODE_" }, value = {
MODE_PRIVATE,
@@ -4267,7 +4276,7 @@ public abstract class Context {
* <p>Note: When implementing this method, keep in mind that new services can be added on newer
* Android releases, so if you're looking for just the explicit names mentioned above, make sure
* to return {@code null} when you don't recognize the name &mdash; if you throw a
- * {@link RuntimeException} exception instead, you're app might break on new Android releases.
+ * {@link RuntimeException} exception instead, your app might break on new Android releases.
*
* @param name The name of the desired service.
*
@@ -7175,7 +7184,7 @@ public abstract class Context {
* <p>
* Applications that run on virtual devices may use this method to access the default device
* capabilities and functionality (by passing
- * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}. Similarly,
+ * {@link Context#DEVICE_ID_DEFAULT}. Similarly,
* applications running on the default device may access the functionality of virtual devices.
* </p>
* <p>
@@ -7542,7 +7551,7 @@ public abstract class Context {
* determine whether they are running on a virtual device and identify that device.
*
* The device ID of the host device is
- * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}
+ * {@link Context#DEVICE_ID_DEFAULT}
*
* <p>
* If the underlying device ID is changed by the system, for example, when an
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5818ed75056c..85daf15865d1 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3234,8 +3234,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast Action: The receiver's effective locale has changed.
*
- * This happens when the device locale, or the receiving app's locale
- * (set via {@link android.app.LocaleManager#setApplicationLocales}) changed.
+ * This happens when the device locale, the receiving app's locale
+ * (set via {@link android.app.LocaleManager#setApplicationLocales}) or language tags
+ * of Regional preferences changed.
*
* Can be received by manifest-declared receivers.
*
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index bd3cf5f0e6bc..afc2285c62c3 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -20,9 +20,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -185,28 +182,6 @@ public class IntentFilter implements Parcelable {
private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
/**
- * An intent with action set as null used to always pass the action test during intent
- * filter matching. This causes a lot of confusion and unexpected intent matches.
- * Null action intents should be blocked when either the intent sender or receiver
- * application targets U or higher.
- *
- * mBlockNullAction indicates whether the intent filter owner (intent receiver) is
- * targeting U+. This value will be properly set by package manager when IntentFilters are
- * passed to an application, so that when an application is trying to perform intent filter
- * matching locally, the correct matching algorithm will be chosen.
- *
- * When an IntentFilter is sent to system server (e.g. for registering runtime receivers),
- * the value set in mBlockNullAction will be ignored and overwritten with the correct
- * value evaluated based on the Binder calling identity. This makes sure that the
- * security enforcement cannot be bypassed by crafting a malicious IntentFilter.
- *
- * @hide
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
- public static final long BLOCK_NULL_ACTION_INTENTS = 264497795;
-
- /**
* The filter {@link #setPriority} value at which system high-priority
* receivers are placed; that is, receivers that should execute before
* application code. Applications should never use filters with this or
@@ -2301,7 +2276,6 @@ public class IntentFilter implements Parcelable {
String type = resolve ? intent.resolveType(resolver) : intent.getType();
return match(intent.getAction(), type, intent.getScheme(),
intent.getData(), intent.getCategories(), logTag,
- CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS),
false /* supportWildcards */, null /* ignoreActions */,
intent.getExtras());
}
@@ -2354,7 +2328,6 @@ public class IntentFilter implements Parcelable {
Uri data, Set<String> categories, String logTag, boolean supportWildcards,
@Nullable Collection<String> ignoreActions) {
return match(action, type, scheme, data, categories, logTag, supportWildcards,
- CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS),
ignoreActions, null /* extras */);
}
@@ -2366,10 +2339,8 @@ public class IntentFilter implements Parcelable {
*/
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag, boolean supportWildcards,
- boolean blockNullAction, @Nullable Collection<String> ignoreActions,
- @Nullable Bundle extras) {
- if ((action == null && blockNullAction)
- || !matchAction(action, supportWildcards, ignoreActions)) {
+ @Nullable Collection<String> ignoreActions, @Nullable Bundle extras) {
+ if (action != null && !matchAction(action, supportWildcards, ignoreActions)) {
if (false) Log.v(
logTag, "No matching action " + action + " for " + this);
return NO_MATCH_ACTION;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a0c620ac26c0..9388823b1a9c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5157,6 +5157,8 @@ public abstract class PackageManager {
* Retrieve overall information about an application package that is
* installed on the system.
*
+ * Use {@link #getPackageInfo(String, PackageInfoFlags)} when long flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of the
* desired package.
* @param flags Additional option flags to modify the data returned.
@@ -5169,9 +5171,7 @@ public abstract class PackageManager {
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if no such package is available to the
* caller.
- * @deprecated Use {@link #getPackageInfo(String, PackageInfoFlags)} instead.
*/
- @Deprecated
public abstract PackageInfo getPackageInfo(@NonNull String packageName, int flags)
throws NameNotFoundException;
@@ -5195,6 +5195,8 @@ public abstract class PackageManager {
* {@link #VERSION_CODE_HIGHEST} in the {@link VersionedPackage}
* constructor.
*
+ * Use {@link #getPackageInfo(VersionedPackage, PackageInfoFlags)} when long flags are needed.
+ *
* @param versionedPackage The versioned package for which to query.
* @param flags Additional option flags to modify the data returned.
* @return A PackageInfo object containing information about the package. If
@@ -5206,9 +5208,7 @@ public abstract class PackageManager {
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if no such package is available to the
* caller.
- * @deprecated Use {@link #getPackageInfo(VersionedPackage, PackageInfoFlags)} instead.
*/
- @Deprecated
public abstract PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
int flags) throws NameNotFoundException;
@@ -5226,6 +5226,8 @@ public abstract class PackageManager {
* Retrieve overall information about an application package that is
* installed on the system.
*
+ * Use {@link #getPackageInfoAsUser(String, PackageInfoFlags, int)} when long flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of the
* desired package.
* @param flags Additional option flags to modify the data returned.
@@ -5239,10 +5241,8 @@ public abstract class PackageManager {
* deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if no such package is available to the
* caller.
- * @deprecated Use {@link #getPackageInfoAsUser(String, PackageInfoFlags, int)} instead.
* @hide
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@UnsupportedAppUsage
@@ -5376,15 +5376,16 @@ public abstract class PackageManager {
* Note that the same package may have different GIDs under different
* {@link UserHandle} on the same device.
*
+ * Use {@link #getPackageGids(String, PackageInfoFlags)} when long flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of the
* desired package.
* @return Returns an int array of the assigned gids, or null if there are
* none.
* @throws NameNotFoundException if no such package is available to the
* caller.
- * @deprecated Use {@link #getPackageGids(String, PackageInfoFlags)} instead.
*/
- @Deprecated
+
public abstract int[] getPackageGids(@NonNull String packageName, int flags)
throws NameNotFoundException;
@@ -5404,14 +5405,14 @@ public abstract class PackageManager {
* Note that the same package will have different UIDs under different
* {@link UserHandle} on the same device.
*
+ * Use {@link #getPackageUid(String, PackageInfoFlags)} when long flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of the
* desired package.
* @return Returns an integer UID who owns the given package name.
* @throws NameNotFoundException if no such package is available to the
* caller.
- * @deprecated Use {@link #getPackageUid(String, PackageInfoFlags)} instead.
*/
- @Deprecated
public abstract int getPackageUid(@NonNull String packageName, int flags)
throws NameNotFoundException;
@@ -5445,10 +5446,9 @@ public abstract class PackageManager {
/**
* See {@link #getPackageUidAsUser(String, PackageInfoFlags, int)}.
- * @deprecated Use {@link #getPackageUidAsUser(String, PackageInfoFlags, int)} instead.
+ * Use {@link #getPackageUidAsUser(String, PackageInfoFlags, int)} when long flags are needed.
* @hide
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
public abstract int getPackageUidAsUser(@NonNull String packageName,
@@ -5589,6 +5589,8 @@ public abstract class PackageManager {
* Retrieve all of the information we know about a particular
* package/application.
*
+ * Use {@link #getApplicationInfo(String, ApplicationInfoFlags)} when long flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of an
* application.
* @param flags Additional option flags to modify the data returned.
@@ -5601,10 +5603,8 @@ public abstract class PackageManager {
* which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
- * @deprecated Use {@link #getApplicationInfo(String, ApplicationInfoFlags)} instead.
*/
@NonNull
- @Deprecated
public abstract ApplicationInfo getApplicationInfo(@NonNull String packageName,
int flags) throws NameNotFoundException;
@@ -5619,13 +5619,13 @@ public abstract class PackageManager {
}
/**
- * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead.
+ * Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} when long flags are
+ * needed.
* {@hide}
*/
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
- @Deprecated
public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
int flags, @UserIdInt int userId) throws NameNotFoundException;
@@ -5642,6 +5642,9 @@ public abstract class PackageManager {
* Retrieve all of the information we know about a particular
* package/application, for a specific user.
*
+ * Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, UserHandle)} when long
+ * flags are needed.
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of an
* application.
* @param flags Additional option flags to modify the data returned.
@@ -5654,14 +5657,11 @@ public abstract class PackageManager {
* which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
- * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, UserHandle)}
- * instead.
* @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
- @Deprecated
public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
int flags, @NonNull UserHandle user)
throws NameNotFoundException {
@@ -5694,6 +5694,8 @@ public abstract class PackageManager {
* Retrieve all of the information we know about a particular activity
* class.
*
+ * Use {@link #getActivityInfo(ComponentName, ComponentInfoFlags)} when long flags are needed.
+ *
* @param component The full component name (i.e.
* com.google.apps.contacts/com.google.apps.contacts.
* ContactsList) of an Activity class.
@@ -5702,9 +5704,7 @@ public abstract class PackageManager {
* activity.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
- * @deprecated Use {@link #getActivityInfo(ComponentName, ComponentInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract ActivityInfo getActivityInfo(@NonNull ComponentName component,
int flags) throws NameNotFoundException;
@@ -5720,20 +5720,11 @@ public abstract class PackageManager {
}
/**
- * @hide
- */
- @NonNull
- public ActivityInfo getActivityInfoAsUser(@NonNull ComponentName component,
- @NonNull ComponentInfoFlags flags, @UserIdInt int userId)
- throws NameNotFoundException {
- throw new UnsupportedOperationException(
- "getActivityInfoAsUser not implemented in subclass");
- }
-
- /**
* Retrieve all of the information we know about a particular receiver
* class.
*
+ * Use {@link #getReceiverInfo(ComponentName, ComponentInfoFlags)} when long flags are needed.
+ *
* @param component The full component name (i.e.
* com.google.apps.calendar/com.google.apps.calendar.
* CalendarAlarm) of a Receiver class.
@@ -5742,9 +5733,7 @@ public abstract class PackageManager {
* receiver.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
- * @deprecated Use {@link #getReceiverInfo(ComponentName, ComponentInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract ActivityInfo getReceiverInfo(@NonNull ComponentName component,
int flags) throws NameNotFoundException;
@@ -5762,6 +5751,8 @@ public abstract class PackageManager {
/**
* Retrieve all of the information we know about a particular service class.
*
+ * Use {@link #getServiceInfo(ComponentName, ComponentInfoFlags)} when long flags are needed.
+ *
* @param component The full component name (i.e.
* com.google.apps.media/com.google.apps.media.
* BackgroundPlayback) of a Service class.
@@ -5769,9 +5760,7 @@ public abstract class PackageManager {
* @return A {@link ServiceInfo} object containing information about the
* service.
* @throws NameNotFoundException if the component cannot be found on the system.
- * @deprecated Use {@link #getServiceInfo(ComponentName, ComponentInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
int flags) throws NameNotFoundException;
@@ -5790,6 +5779,8 @@ public abstract class PackageManager {
* Retrieve all of the information we know about a particular content
* provider class.
*
+ * Use {@link #getProviderInfo(ComponentName, ComponentInfoFlags)} when long flags are needed.
+ *
* @param component The full component name (i.e.
* com.google.providers.media/com.google.providers.media.
* MediaProvider) of a ContentProvider class.
@@ -5798,9 +5789,7 @@ public abstract class PackageManager {
* provider.
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
- * @deprecated Use {@link #getProviderInfo(ComponentName, ComponentInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract ProviderInfo getProviderInfo(@NonNull ComponentName component,
int flags) throws NameNotFoundException;
@@ -5849,6 +5838,8 @@ public abstract class PackageManager {
/**
* Return a List of all packages that are installed for the current user.
*
+ * Use {@link #getInstalledPackages(PackageInfoFlags)} when long flags are needed.
+ *
* @param flags Additional option flags to modify the data returned.
* @return A List of PackageInfo objects, one for each installed package,
* containing information about the package. In the unlikely case
@@ -5858,15 +5849,12 @@ public abstract class PackageManager {
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
- * @deprecated Use {@link #getInstalledPackages(PackageInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<PackageInfo> getInstalledPackages(int flags);
/**
* See {@link #getInstalledPackages(int)}.
- * @param flags
*/
@NonNull
public List<PackageInfo> getInstalledPackages(@NonNull PackageInfoFlags flags) {
@@ -5896,6 +5884,9 @@ public abstract class PackageManager {
* Return a List of all installed packages that are currently holding any of
* the given permissions.
*
+ * Use {@link #getPackagesHoldingPermissions(String[], PackageInfoFlags)} when long flags are
+ * needed.
+ *
* @param flags Additional option flags to modify the data returned.
* @return A List of PackageInfo objects, one for each installed package
* that holds any of the permissions that were provided, containing
@@ -5906,9 +5897,7 @@ public abstract class PackageManager {
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
- * @deprecated Use {@link #getPackagesHoldingPermissions(String[], PackageInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<PackageInfo> getPackagesHoldingPermissions(
@NonNull String[] permissions, int flags);
@@ -5927,6 +5916,8 @@ public abstract class PackageManager {
* Return a List of all packages that are installed on the device, for a
* specific user.
*
+ * Use {@link #getInstalledPackagesAsUser(PackageInfoFlags, int)} when long flags are needed.
+ *
* @param flags Additional option flags to modify the data returned.
* @param userId The user for whom the installed packages are to be listed
* @return A List of PackageInfo objects, one for each installed package,
@@ -5937,10 +5928,8 @@ public abstract class PackageManager {
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
- * @deprecated Use {@link #getInstalledPackagesAsUser(PackageInfoFlags, int)} instead.
* @hide
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@@ -6652,6 +6641,8 @@ public abstract class PackageManager {
* applications including those deleted with {@code DELETE_KEEP_DATA}
* (partially installed apps with data directory) will be returned.
*
+ * Use {@link #getInstalledApplications(ApplicationInfoFlags)} when long flags are needed.
+ *
* @param flags Additional option flags to modify the data returned.
* @return A List of ApplicationInfo objects, one for each installed
* application. In the unlikely case there are no installed
@@ -6661,10 +6652,8 @@ public abstract class PackageManager {
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
- * @deprecated Use {@link #getInstalledApplications(ApplicationInfoFlags)} instead.
*/
@NonNull
- @Deprecated
public abstract List<ApplicationInfo> getInstalledApplications(int flags);
/**
@@ -6683,6 +6672,9 @@ public abstract class PackageManager {
* {@code DELETE_KEEP_DATA} (partially installed apps with data directory)
* will be returned.
*
+ * Use {@link #getInstalledApplicationsAsUser(ApplicationInfoFlags, int)} when long flags are
+ * needed.
+ *
* @param flags Additional option flags to modify the data returned.
* @param userId The user for whom the installed applications are to be
* listed
@@ -6694,13 +6686,11 @@ public abstract class PackageManager {
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DELETE_KEEP_DATA} flag set).
- * @deprecated Use {@link #getInstalledApplicationsAsUser(ApplicationInfoFlags, int)} instead.
* @hide
*/
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@TestApi
- @Deprecated
public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
int flags, @UserIdInt int userId);
@@ -6859,13 +6849,13 @@ public abstract class PackageManager {
/**
* Get a list of shared libraries on the device.
*
+ * Use {@link #getSharedLibraries(PackageInfoFlags)} when long flags are needed.
+ *
* @param flags To filter the libraries to return.
* @return The shared library list.
*
* @see #MATCH_UNINSTALLED_PACKAGES
- * @deprecated Use {@link #getSharedLibraries(PackageInfoFlags)} instead.
*/
- @Deprecated
public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags);
/**
@@ -6880,6 +6870,8 @@ public abstract class PackageManager {
/**
* Get a list of shared libraries on the device.
*
+ * Use {@link #getSharedLibrariesAsUser(PackageInfoFlags, int)} when long flags are needed.
+ *
* @param flags To filter the libraries to return.
* @param userId The user to query for.
* @return The shared library list.
@@ -6890,9 +6882,7 @@ public abstract class PackageManager {
* @see #MATCH_UNINSTALLED_PACKAGES
*
* @hide
- * @deprecated Use {@link #getSharedLibrariesAsUser(PackageInfoFlags, int)} instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags,
@UserIdInt int userId);
@@ -6910,14 +6900,14 @@ public abstract class PackageManager {
/**
* Get the list of shared libraries declared by a package.
*
+ * Use {@link #getDeclaredSharedLibraries(String, PackageInfoFlags)} when long flags are needed.
+ *
* @param packageName the package name to query
* @param flags the flags to filter packages
* @return the shared library list
*
* @hide
- * @deprecated Use {@link #getDeclaredSharedLibraries(String, PackageInfoFlags)} instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
@@ -7026,6 +7016,8 @@ public abstract class PackageManager {
* Intent.resolveActivity(PackageManager)} do.
* </p>
*
+ * Use {@link #resolveActivity(Intent, ResolveInfoFlags)} when long flags are needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned. The
@@ -7037,9 +7029,7 @@ public abstract class PackageManager {
* matching activity was found. If multiple matching activities are
* found and there is no default set, returns a ResolveInfo object
* containing something else, such as the activity resolver.
- * @deprecated Use {@link #resolveActivity(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@Nullable
public abstract ResolveInfo resolveActivity(@NonNull Intent intent, int flags);
@@ -7066,6 +7056,8 @@ public abstract class PackageManager {
* Intent.resolveActivity(PackageManager)} do.
* </p>
*
+ * Use {@link #resolveActivityAsUser(Intent, ResolveInfoFlags, int)} when long flags are needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned. The
@@ -7079,9 +7071,7 @@ public abstract class PackageManager {
* found and there is no default set, returns a ResolveInfo object
* containing something else, such as the activity resolver.
* @hide
- * @deprecated Use {@link #resolveActivityAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
@UnsupportedAppUsage
@@ -7102,6 +7092,8 @@ public abstract class PackageManager {
/**
* Retrieve all activities that can be performed for the given intent.
*
+ * Use {@link #queryIntentActivities(Intent, ResolveInfoFlags)} when long flags are needed.
+ *
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned. The
* most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
@@ -7113,9 +7105,7 @@ public abstract class PackageManager {
* words, the first item is what would be returned by
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
- * @deprecated Use {@link #queryIntentActivities(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent, int flags);
@@ -7133,6 +7123,9 @@ public abstract class PackageManager {
* Retrieve all activities that can be performed for the given intent, for a
* specific user.
*
+ * Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, int)} when long flags are
+ * needed.
+ *
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned. The
* most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
@@ -7145,9 +7138,7 @@ public abstract class PackageManager {
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
* @hide
- * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
@@ -7169,6 +7160,9 @@ public abstract class PackageManager {
* Retrieve all activities that can be performed for the given intent, for a
* specific user.
*
+ * Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, UserHandle)} when long
+ * flags are needed.
+ *
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned. The
* most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
@@ -7182,10 +7176,7 @@ public abstract class PackageManager {
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
* @hide
- * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, UserHandle)}
- * instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@@ -7215,6 +7206,9 @@ public abstract class PackageManager {
* final ResolveInfo list in a reasonable order, with no duplicates, based
* on those inputs.
*
+ * Use {@link #queryIntentActivityOptions(ComponentName, List, Intent, ResolveInfoFlags)} when
+ * long flags are needed.
+ *
* @param caller The class name of the activity that is making the request.
* This activity will never appear in the output list. Can be
* null.
@@ -7231,10 +7225,7 @@ public abstract class PackageManager {
* activities that can handle <var>intent</var> but did not get
* included by one of the <var>specifics</var> intents. If there are
* no matching activities, an empty list is returned.
- * @deprecated Use {@link #queryIntentActivityOptions(ComponentName, List, Intent,
- * ResolveInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
@Nullable Intent[] specifics, @NonNull Intent intent, int flags);
@@ -7253,14 +7244,14 @@ public abstract class PackageManager {
/**
* Retrieve all receivers that can handle a broadcast of the given intent.
*
+ * Use {@link #queryBroadcastReceivers(Intent, ResolveInfoFlags)} when long flags are needed.
+ *
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching receiver, ordered from best to worst. If there are
* no matching receivers, an empty list or null is returned.
- * @deprecated Use {@link #queryBroadcastReceivers(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent, int flags);
@@ -7278,6 +7269,9 @@ public abstract class PackageManager {
* Retrieve all receivers that can handle a broadcast of the given intent,
* for a specific user.
*
+ * Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, UserHandle)} when long
+ * flags are needed.
+ *
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned.
* @param userHandle UserHandle of the user being queried.
@@ -7285,10 +7279,7 @@ public abstract class PackageManager {
* each matching receiver, ordered from best to worst. If there are
* no matching receivers, an empty list or null is returned.
* @hide
- * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, UserHandle)}
- * instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@@ -7312,10 +7303,9 @@ public abstract class PackageManager {
/**
* @hide
- * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, int)}
- * instead.
+ * Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, int)} when long flags are
+ * needed.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
@@ -7353,15 +7343,15 @@ public abstract class PackageManager {
/**
* Determine the best service to handle for a given Intent.
*
+ * Use {@link #resolveService(Intent, ResolveInfoFlags)} when long flags are needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
* @return Returns a ResolveInfo object containing the final service intent
* that was determined to be the best action. Returns null if no
* matching service was found.
- * @deprecated Use {@link #resolveService(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@Nullable
public abstract ResolveInfo resolveService(@NonNull Intent intent, int flags);
@@ -7376,9 +7366,8 @@ public abstract class PackageManager {
/**
* @hide
- * @deprecated Use {@link #resolveServiceAsUser(Intent, ResolveInfoFlags, int)} instead.
+ * Use {@link #resolveServiceAsUser(Intent, ResolveInfoFlags, int)} when long flags are needed.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
@@ -7398,6 +7387,8 @@ public abstract class PackageManager {
/**
* Retrieve all services that can match the given intent.
*
+ * Use {@link #queryIntentServices(Intent, ResolveInfoFlags)} when long flags are needed.
+ *
* @param intent The desired intent as per resolveService().
* @param flags Additional option flags to modify the data returned.
* @return Returns a List of ResolveInfo objects containing one entry for
@@ -7405,9 +7396,7 @@ public abstract class PackageManager {
* words, the first item is what would be returned by
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
- * @deprecated Use {@link #queryIntentServices(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
int flags);
@@ -7425,6 +7414,9 @@ public abstract class PackageManager {
/**
* Retrieve all services that can match the given intent for a given user.
*
+ * Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, int)} when long flags are
+ * needed.
+ *
* @param intent The desired intent as per resolveService().
* @param flags Additional option flags to modify the data returned.
* @param userId The user id.
@@ -7434,9 +7426,7 @@ public abstract class PackageManager {
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
* @hide
- * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, int)} instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
@@ -7457,6 +7447,9 @@ public abstract class PackageManager {
/**
* Retrieve all services that can match the given intent for a given user.
*
+ * Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, UserHandle)} when long flags
+ * are needed.
+ *
* @param intent The desired intent as per resolveService().
* @param flags Additional option flags to modify the data returned.
* @param user The user being queried.
@@ -7466,10 +7459,7 @@ public abstract class PackageManager {
* {@link #resolveService}. If there are no matching services, an
* empty list or null is returned.
* @hide
- * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, UserHandle)}
- * instead.
*/
- @Deprecated
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
@@ -7492,6 +7482,9 @@ public abstract class PackageManager {
/**
* Retrieve all providers that can match the given intent.
*
+ * Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags, int)} when long flags
+ * are needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
@@ -7500,10 +7493,7 @@ public abstract class PackageManager {
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
* @hide
- * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags, int)}
- * instead.
*/
- @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@UnsupportedAppUsage
@@ -7524,6 +7514,9 @@ public abstract class PackageManager {
/**
* Retrieve all providers that can match the given intent.
*
+ * Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags, UserHandle)} when
+ * long flags are needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
@@ -7532,10 +7525,7 @@ public abstract class PackageManager {
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
* @hide
- * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags,
- * UserHandle)} instead.
*/
- @Deprecated
@NonNull
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
@@ -7559,15 +7549,16 @@ public abstract class PackageManager {
/**
* Retrieve all providers that can match the given intent.
*
+ * Use {@link #queryIntentContentProviders(Intent, ResolveInfoFlags)} when long flags are
+ * needed.
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags to modify the data returned.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching provider, ordered from best to worst. If there are
* no matching services, an empty list or null is returned.
- * @deprecated Use {@link #queryIntentContentProviders(Intent, ResolveInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
int flags);
@@ -7591,13 +7582,13 @@ public abstract class PackageManager {
* ProviderInfo info = packageManager.resolveContentProvider(uri.getAuthority(), flags);
* </pre>
*
+ * Use {@link #resolveContentProvider(String, ComponentInfoFlags)} when long flags are needed.
+ *
* @param authority The authority of the provider to find.
* @param flags Additional option flags to modify the data returned.
* @return A {@link ProviderInfo} object containing information about the
* provider. If a provider was not found, returns null.
- * @deprecated Use {@link #resolveContentProvider(String, ComponentInfoFlags)} instead.
*/
- @Deprecated
@Nullable
public abstract ProviderInfo resolveContentProvider(@NonNull String authority,
int flags);
@@ -7615,14 +7606,15 @@ public abstract class PackageManager {
/**
* Find a single content provider by its base path name.
*
+ * Use {@link #resolveContentProviderAsUser(String, ComponentInfoFlags, int)} when long flags
+ * are needed.
+ *
* @param providerName The name of the provider to find.
* @param flags Additional option flags to modify the data returned.
* @param userId The user id.
* @return A {@link ProviderInfo} object containing information about the
* provider. If a provider was not found, returns null.
* @hide
- * @deprecated Use {@link #resolveContentProviderAsUser(String, ComponentInfoFlags, int)}
- * instead.
*/
@SuppressWarnings("HiddenAbstractMethod")
@Nullable
@@ -7647,6 +7639,9 @@ public abstract class PackageManager {
* <em>Note: unlike most other methods, an empty result set is indicated
* by a null return instead of an empty list.</em>
*
+ * Use {@link #queryContentProviders(String, int, ComponentInfoFlags)} when long flags are
+ * needed.
+ *
* @param processName If non-null, limits the returned providers to only
* those that are hosted by the given process. If null, all
* content providers are returned.
@@ -7657,9 +7652,7 @@ public abstract class PackageManager {
* each provider either matching <var>processName</var> or, if
* <var>processName</var> is null, all known content providers.
* <em>If there are no matching providers, null is returned.</em>
- * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags)} instead.
*/
- @Deprecated
@NonNull
public abstract List<ProviderInfo> queryContentProviders(
@Nullable String processName, int uid, int flags);
@@ -7687,11 +7680,11 @@ public abstract class PackageManager {
* to mark GAL providers, rather than intent filters, so we can't use
* {@link #queryIntentContentProviders} for that.
*
+ * Use {@link #queryContentProviders(String, int, ComponentInfoFlags, String)} when long flags
+ * are needed.
+ *
* @hide
- * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags, String)}
- * instead.
*/
- @Deprecated
@NonNull
public List<ProviderInfo> queryContentProviders(@Nullable String processName,
int uid, int flags, String metaDataKey) {
@@ -8237,13 +8230,13 @@ public abstract class PackageManager {
* Retrieve overall information about an application package defined in a
* package archive file
*
+ * Use {@link #getPackageArchiveInfo(String, PackageInfoFlags)} when long flags are needed.
+ *
* @param archiveFilePath The path to the archive file
* @param flags Additional option flags to modify the data returned.
* @return A PackageInfo object containing information about the package
* archive. If the package could not be parsed, returns null.
- * @deprecated Use {@link #getPackageArchiveInfo(String, PackageInfoFlags)} instead.
*/
- @Deprecated
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 02b930860a84..1a3c3d97634c 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -376,8 +376,8 @@ public class ServiceInfo extends ComponentInfo
* <li>Headless system apps</li>
* <li><a href="{@docRoot}guide/topics/admin/device-admin">Device admin apps</a></li>
* <li>Active VPN apps</li>
- * <li>Apps holding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} or
- * {@link Manifest.permission#USE_EXACT_ALARM} permission.</li>
+ * <li>Apps holding {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} or
+ * {@link android.Manifest.permission#USE_EXACT_ALARM} permission.</li>
* </ul>
* </p>
*/
@@ -393,7 +393,7 @@ public class ServiceInfo extends ComponentInfo
*
* <p>Unlike other foreground service types, this type is not associated with a specific use
* case, and it will not require any special permissions
- * (besides {@link Manifest.permission#FOREGROUND_SERVICE}).
+ * (besides {@link android.Manifest.permission#FOREGROUND_SERVICE}).
*
* However, this type has the following restrictions.
*
@@ -401,19 +401,21 @@ public class ServiceInfo extends ComponentInfo
* <li>
* The type has a 3 minute timeout.
* A foreground service of this type must be stopped within the timeout by
- * {@link android.app.Service#stopSelf),
- * or {@link android.content.Context#stopService).
- * {@link android.app.Service#stopForeground) will also work, which will demote the
+ * {@link android.app.Service#stopSelf()},
+ * {@link android.content.Context#stopService(android.content.Intent)}
+ * or their overloads).
+ * {@link android.app.Service#stopForeground(int)} will also work,
+ * which will demote the
* service to a "background" service, which will soon be stopped by the system.
*
- * <p>The system will <em>not</em> automatically stop it.
- *
* <p>If the service isn't stopped within the timeout,
- * {@link android.app.Service#onTimeout(int)} will be called.
+ * {@link android.app.Service#onTimeout(int)} will be called. Note, even when the
+ * system calls this callback, it will not stop the service automatically.
+ * You still need to stop the service using one of the aforementioned
+ * ways even when you get this callback.
*
* <p>If the service is still not stopped after the callback,
- * the app will be declared an ANR after a short grace period of several seconds.
- *
+ * the app will be declared an ANR, after a short grace period of several seconds.
* <li>
* A foreground service of this type cannot be made "sticky"
* (see {@link android.app.Service#START_STICKY}). That is, if an app is killed
@@ -427,10 +429,26 @@ public class ServiceInfo extends ComponentInfo
* <a href="/guide/components/foreground-services#background-start-restrictions>
* Restrictions on background starts
* </a>
+ * <li>
+ * You can combine multiple foreground services types with {@code |}s, and you can
+ * combine
+ * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}.
+ * with other types as well.
+ * However,
+ * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}
+ * is for situations
+ * where you have no other valid foreground services to use and the timeout is long
+ * enough for the task, and if you can use other types, there's no point using
+ * this type.
+ * For this reason, if
+ * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}
+ * is combined with other foreground service types, the system will simply ignore
+ * it, and as a result,
+ * none of the above restrictions will apply (e.g. there'll be no timeout).
* </ul>
*
- * <p>Note, even though
- * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}
+ * <p>Also note, even though
+ * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}
* was added
* on Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
* it can be also used on
diff --git a/core/java/android/content/pm/UserPackage.java b/core/java/android/content/pm/UserPackage.java
index 7ca92c3d4777..c35e67801b71 100644
--- a/core/java/android/content/pm/UserPackage.java
+++ b/core/java/android/content/pm/UserPackage.java
@@ -18,14 +18,16 @@ package android.content.pm;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.os.Process;
-import android.os.UserHandle;
import android.util.SparseArrayMap;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import libcore.util.EmptyArray;
+
import java.util.Objects;
+import java.util.Random;
/**
* POJO to represent a package for a specific user ID.
@@ -34,6 +36,16 @@ import java.util.Objects;
*/
public final class UserPackage {
private static final boolean ENABLE_CACHING = true;
+ /**
+ * The maximum number of entries to keep in the cache per user ID.
+ * The value should ideally be high enough to cover all packages on an end-user device,
+ * but low enough that stale or invalid packages would eventually (probably) get removed.
+ * This should benefit components that loop through all packages on a device and use this class,
+ * since being able to cache the objects for all packages on the device
+ * means we don't have to keep recreating the objects.
+ */
+ @VisibleForTesting
+ static final int MAX_NUM_CACHED_ENTRIES_PER_USER = 1000;
@UserIdInt
public final int userId;
@@ -43,11 +55,13 @@ public final class UserPackage {
@GuardedBy("sCacheLock")
private static final SparseArrayMap<String, UserPackage> sCache = new SparseArrayMap<>();
- private static final class NoPreloadHolder {
- /** Set of userIDs to cache objects for. */
- @GuardedBy("sCacheLock")
- private static int[] sUserIds = new int[]{UserHandle.getUserId(Process.myUid())};
- }
+ /**
+ * Set of userIDs to cache objects for. We start off with an empty set, so there's no caching
+ * by default. The system will override with a valid set of userIDs in its process so that
+ * caching becomes active in the system process.
+ */
+ @GuardedBy("sCacheLock")
+ private static int[] sUserIds = EmptyArray.INT;
private UserPackage(int userId, String packageName) {
this.userId = userId;
@@ -87,13 +101,14 @@ public final class UserPackage {
}
synchronized (sCacheLock) {
- if (!ArrayUtils.contains(NoPreloadHolder.sUserIds, userId)) {
+ if (!ArrayUtils.contains(sUserIds, userId)) {
// Don't cache objects for invalid userIds.
return new UserPackage(userId, packageName);
}
UserPackage up = sCache.get(userId, packageName);
if (up == null) {
+ maybePurgeRandomEntriesLocked(userId);
packageName = packageName.intern();
up = new UserPackage(userId, packageName);
sCache.add(userId, packageName, up);
@@ -121,7 +136,7 @@ public final class UserPackage {
userIds = userIds.clone();
synchronized (sCacheLock) {
- NoPreloadHolder.sUserIds = userIds;
+ sUserIds = userIds;
for (int u = sCache.numMaps() - 1; u >= 0; --u) {
final int userId = sCache.keyAt(u);
@@ -131,4 +146,35 @@ public final class UserPackage {
}
}
}
+
+ @VisibleForTesting
+ public static int numEntriesForUser(int userId) {
+ synchronized (sCacheLock) {
+ return sCache.numElementsForKey(userId);
+ }
+ }
+
+ /** Purge a random set of entries if the cache size is too large. */
+ @GuardedBy("sCacheLock")
+ private static void maybePurgeRandomEntriesLocked(int userId) {
+ final int uIdx = sCache.indexOfKey(userId);
+ if (uIdx < 0) {
+ return;
+ }
+ int numCached = sCache.numElementsForKeyAt(uIdx);
+ if (numCached < MAX_NUM_CACHED_ENTRIES_PER_USER) {
+ return;
+ }
+ // Purge a random set of 1% of cached elements for the userId. We don't want to use a
+ // deterministic system of purging because that may cause us to repeatedly remove elements
+ // that are frequently added and queried more than others. Choosing a random set
+ // means we will probably eventually remove less useful elements.
+ // An LRU cache is too expensive for this commonly used utility class.
+ final Random rand = new Random();
+ final int numToPurge = Math.max(1, MAX_NUM_CACHED_ENTRIES_PER_USER / 100);
+ for (int i = 0; i < numToPurge && numCached > 0; ++i) {
+ final int removeIdx = rand.nextInt(numCached--);
+ sCache.deleteAt(uIdx, removeIdx);
+ }
+ }
}
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index ffdc7b3807cf..6b09c303e3cd 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -18,6 +18,7 @@ package android.content.res;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.MathUtils;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -98,22 +99,81 @@ public class FontScaleConverterFactory {
public static FontScaleConverter forScale(float fontScale) {
if (fontScale <= 1) {
// We don't need non-linear curves for shrinking text or for 100%.
- // Also, fontScale==0 should not have a curve either
+ // Also, fontScale==0 should not have a curve either.
+ // And ignore negative font scales; that's just silly.
return null;
}
FontScaleConverter lookupTable = get(fontScale);
- // TODO(b/247861716): interpolate between two tables when null
+ if (lookupTable != null) {
+ return lookupTable;
+ }
+
+ // Didn't find an exact match: interpolate between two existing tables
+ final int index = LOOKUP_TABLES.indexOfKey(getKey(fontScale));
+ if (index >= 0) {
+ // This should never happen, should have been covered by get() above.
+ return LOOKUP_TABLES.valueAt(index);
+ }
+ // Didn't find an exact match: interpolate between two existing tables
+ final int lowerIndex = -(index + 1) - 1;
+ final int higherIndex = lowerIndex + 1;
+ if (lowerIndex < 0 || higherIndex >= LOOKUP_TABLES.size()) {
+ // We have gone beyond our bounds and have nothing to interpolate between. Just give
+ // them a straight linear table instead.
+ // This works because when FontScaleConverter encounters a size beyond its bounds, it
+ // calculates a linear fontScale factor using the ratio of the last element pair.
+ return new FontScaleConverter(new float[] {1f}, new float[] {fontScale});
+ } else {
+ float startScale = getScaleFromKey(LOOKUP_TABLES.keyAt(lowerIndex));
+ float endScale = getScaleFromKey(LOOKUP_TABLES.keyAt(higherIndex));
+ float interpolationPoint = MathUtils.constrainedMap(
+ /* rangeMin= */ 0f,
+ /* rangeMax= */ 1f,
+ startScale,
+ endScale,
+ fontScale
+ );
+ return createInterpolatedTableBetween(
+ LOOKUP_TABLES.valueAt(lowerIndex),
+ LOOKUP_TABLES.valueAt(higherIndex),
+ interpolationPoint);
+ }
+ }
+
+ @NonNull
+ private static FontScaleConverter createInterpolatedTableBetween(
+ FontScaleConverter start,
+ FontScaleConverter end,
+ float interpolationPoint
+ ) {
+ float[] commonSpSizes = new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100f};
+ float[] dpInterpolated = new float[commonSpSizes.length];
+
+ for (int i = 0; i < commonSpSizes.length; i++) {
+ float sp = commonSpSizes[i];
+ float startDp = start.convertSpToDp(sp);
+ float endDp = end.convertSpToDp(sp);
+ dpInterpolated[i] = MathUtils.lerp(startDp, endDp, interpolationPoint);
+ }
+
+ return new FontScaleConverter(commonSpSizes, dpInterpolated);
+ }
+
+ private static int getKey(float fontScale) {
+ return (int) (fontScale * SCALE_KEY_MULTIPLIER);
+ }
- return lookupTable;
+ private static float getScaleFromKey(int key) {
+ return (float) key / SCALE_KEY_MULTIPLIER;
}
private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) {
- LOOKUP_TABLES.put((int) (scaleKey * SCALE_KEY_MULTIPLIER), fontScaleConverter);
+ LOOKUP_TABLES.put(getKey(scaleKey), fontScaleConverter);
}
@Nullable
private static FontScaleConverter get(float scaleKey) {
- return LOOKUP_TABLES.get((int) (scaleKey * SCALE_KEY_MULTIPLIER));
+ return LOOKUP_TABLES.get(getKey(scaleKey));
}
}
diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS
index d12d920b2a54..a7bce122eb35 100644
--- a/core/java/android/content/res/OWNERS
+++ b/core/java/android/content/res/OWNERS
@@ -4,3 +4,5 @@ toddke@android.com
toddke@google.com
patb@google.com
zyy@google.com
+
+per-file FontScaleConverter*=fuego@google.com \ No newline at end of file
diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java
index b756a4323842..c89a5c62cd58 100644
--- a/core/java/android/credentials/CreateCredentialRequest.java
+++ b/core/java/android/credentials/CreateCredentialRequest.java
@@ -244,7 +244,7 @@ public final class CreateCredentialRequest implements Parcelable {
/** A builder for {@link CreateCredentialRequest}. */
public static final class Builder {
- private boolean mAlwaysSendAppInfoToProvider;
+ private boolean mAlwaysSendAppInfoToProvider = true;
@NonNull
private String mType;
diff --git a/core/java/android/credentials/ui/CancelUiRequest.java b/core/java/android/credentials/ui/CancelUiRequest.java
new file mode 100644
index 000000000000..6bd9de481a79
--- /dev/null
+++ b/core/java/android/credentials/ui/CancelUiRequest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.credentials.ui;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * A request to cancel any ongoing UI matching this request.
+ *
+ * @hide
+ */
+public final class CancelUiRequest implements Parcelable {
+
+ /**
+ * The intent extra key for the {@code CancelUiRequest} object when launching the UX
+ * activities.
+ */
+ @NonNull public static final String EXTRA_CANCEL_UI_REQUEST =
+ "android.credentials.ui.extra.EXTRA_CANCEL_UI_REQUEST";
+
+ @NonNull
+ private final IBinder mToken;
+
+ /** Returns the request token matching the user request that should be cancelled. */
+ @NonNull
+ public IBinder getToken() {
+ return mToken;
+ }
+
+ public CancelUiRequest(@NonNull IBinder token) {
+ mToken = token;
+ }
+
+ private CancelUiRequest(@NonNull Parcel in) {
+ mToken = in.readStrongBinder();
+ AnnotationValidations.validate(NonNull.class, null, mToken);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mToken);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull public static final Creator<CancelUiRequest> CREATOR = new Creator<>() {
+ @Override
+ public CancelUiRequest createFromParcel(@NonNull Parcel in) {
+ return new CancelUiRequest(in);
+ }
+
+ @Override
+ public CancelUiRequest[] newArray(int size) {
+ return new CancelUiRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index 67634dc0ad8c..dcfef56f86a4 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -22,6 +22,7 @@ import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.ResultReceiver;
@@ -66,6 +67,25 @@ public class IntentFactory {
}
/**
+ * Creates an Intent that cancels any UI matching the given request token id.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Intent createCancelUiIntent(@NonNull IBinder requestToken) {
+ Intent intent = new Intent();
+ ComponentName componentName =
+ ComponentName.unflattenFromString(
+ Resources.getSystem()
+ .getString(
+ com.android.internal.R.string
+ .config_credentialManagerDialogComponent));
+ intent.setComponent(componentName);
+ intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, new CancelUiRequest(requestToken));
+ return intent;
+ }
+
+ /**
* Notify the UI that providers have been enabled/disabled.
*
* @hide
@@ -78,7 +98,7 @@ public class IntentFactory {
Resources.getSystem()
.getString(
com.android.internal.R.string
- .config_credentialManagerDialogComponent));
+ .config_credentialManagerReceiverComponent));
intent.setComponent(componentName);
intent.setAction(Constants.CREDMAN_ENABLED_PROVIDERS_UPDATED);
return intent;
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 9388ae3fd5e4..73157e62cb56 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -17,10 +17,10 @@
package android.hardware;
import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.companion.virtual.VirtualDeviceManager;
@@ -92,7 +92,8 @@ public class SystemSensorManager extends SensorManager {
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
private static native int nativeCreateDirectChannel(
- long nativeInstance, long size, int channelType, int fd, HardwareBuffer buffer);
+ long nativeInstance, int deviceId, long size, int channelType, int fd,
+ HardwareBuffer buffer);
private static native void nativeDestroyDirectChannel(
long nativeInstance, int channelHandle);
private static native int nativeConfigDirectChannel(
@@ -695,6 +696,10 @@ public class SystemSensorManager extends SensorManager {
/** @hide */
protected SensorDirectChannel createDirectChannelImpl(
MemoryFile memoryFile, HardwareBuffer hardwareBuffer) {
+ int deviceId = mContext.getDeviceId();
+ if (isDeviceSensorPolicyDefault(deviceId)) {
+ deviceId = DEVICE_ID_DEFAULT;
+ }
int id;
int type;
long size;
@@ -713,8 +718,8 @@ public class SystemSensorManager extends SensorManager {
}
size = memoryFile.length();
- id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_MEMORY_FILE, fd, null);
+ id = nativeCreateDirectChannel(mNativeInstance, deviceId, size,
+ SensorDirectChannel.TYPE_MEMORY_FILE, fd, null);
if (id <= 0) {
throw new UncheckedIOException(
new IOException("create MemoryFile direct channel failed " + id));
@@ -738,7 +743,7 @@ public class SystemSensorManager extends SensorManager {
}
size = hardwareBuffer.getWidth();
id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+ mNativeInstance, deviceId, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
-1, hardwareBuffer);
if (id <= 0) {
throw new UncheckedIOException(
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 19719a88ab2e..fbc018444dc5 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -142,24 +142,18 @@ public final class CameraManager {
PackageManager.PERMISSION_GRANTED;
}
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
mFoldStateListener = new FoldStateListener(context);
try {
- context.getSystemService(DeviceStateManager.class)
- .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
+ context.getSystemService(DeviceStateManager.class).registerCallback(
+ new HandlerExecutor(CameraManagerGlobal.get().getDeviceStateHandler()),
+ mFoldStateListener);
} catch (IllegalStateException e) {
Log.v(TAG, "Failed to register device state listener!");
Log.v(TAG, "Device state dependent characteristics updates will not be functional!");
- mHandlerThread.quitSafely();
- mHandler = null;
mFoldStateListener = null;
}
}
- private HandlerThread mHandlerThread;
- private Handler mHandler;
private FoldStateListener mFoldStateListener;
/**
@@ -1645,6 +1639,9 @@ public final class CameraManager {
private ICameraService mCameraService;
private boolean mHasOpenCloseListenerPermission = false;
+ private HandlerThread mDeviceStateHandlerThread;
+ private Handler mDeviceStateHandler;
+
// Singleton, don't allow construction
private CameraManagerGlobal() { }
@@ -1658,6 +1655,18 @@ public final class CameraManager {
return gCameraManager;
}
+ public Handler getDeviceStateHandler() {
+ synchronized(mLock) {
+ if (mDeviceStateHandlerThread == null) {
+ mDeviceStateHandlerThread = new HandlerThread(TAG);
+ mDeviceStateHandlerThread.start();
+ mDeviceStateHandler = new Handler(mDeviceStateHandlerThread.getLooper());
+ }
+
+ return mDeviceStateHandler;
+ }
+ }
+
@Override
public IBinder asBinder() {
return this;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b766cd19cdb0..50dd7a0bc1be 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -989,24 +989,6 @@ public final class DisplayManager {
/**
* Creates a virtual display.
- *
- * @see #createVirtualDisplay(String, int, int, int, float, Surface, int,
- * Handler, VirtualDisplay.Callback)
- */
- @Nullable
- public VirtualDisplay createVirtualDisplay(@NonNull String name,
- @IntRange(from = 1) int width,
- @IntRange(from = 1) int height,
- @IntRange(from = 1) int densityDpi,
- float requestedRefreshRate,
- @Nullable Surface surface,
- @VirtualDisplayFlag int flags) {
- return createVirtualDisplay(name, width, height, densityDpi, requestedRefreshRate,
- surface, flags, null, null);
- }
-
- /**
- * Creates a virtual display.
* <p>
* The content of a virtual display is rendered to a {@link Surface} provided
* by the application.
@@ -1056,8 +1038,23 @@ public final class DisplayManager {
@VirtualDisplayFlag int flags,
@Nullable VirtualDisplay.Callback callback,
@Nullable Handler handler) {
- return createVirtualDisplay(name, width, height, densityDpi, 0.0f, surface,
- flags, handler, callback);
+ final VirtualDisplayConfig.Builder builder =
+ new VirtualDisplayConfig.Builder(name, width, height, densityDpi);
+ builder.setFlags(flags);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return createVirtualDisplay(builder.build(), handler, callback);
+ }
+
+ /**
+ * Creates a virtual display.
+ *
+ * @see #createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback)
+ */
+ @Nullable
+ public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig config) {
+ return createVirtualDisplay(config, /*handler=*/null, /*callback=*/null);
}
/**
@@ -1084,21 +1081,7 @@ public final class DisplayManager {
* turning off the screen.
* </p>
*
- * @param name The name of the virtual display, must be non-empty.
- * @param width The width of the virtual display in pixels, must be greater than 0.
- * @param height The height of the virtual display in pixels, must be greater than 0.
- * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
- * @param requestedRefreshRate The requested refresh rate in frames per second.
- * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
- * 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded
- * up or down to a divisor of the physical display. If 0 is specified, the virtual
- * display is refreshed at the physical display refresh rate.
- * @param surface The surface to which the content of the virtual display should
- * be rendered, or null if there is none initially.
- * @param flags A combination of virtual display flags:
- * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION},
- * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
- * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
+ * @param config The configuration of the virtual display, must be non-null.
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
@@ -1106,33 +1089,14 @@ public final class DisplayManager {
* not create the virtual display.
*
* @throws SecurityException if the caller does not have permission to create
- * a virtual display with the specified flags.
+ * a virtual display with flags specified in the configuration.
*/
@Nullable
- public VirtualDisplay createVirtualDisplay(@NonNull String name,
- @IntRange(from = 1) int width,
- @IntRange(from = 1) int height,
- @IntRange(from = 1) int densityDpi,
- float requestedRefreshRate,
- @Nullable Surface surface,
- @VirtualDisplayFlag int flags,
+ public VirtualDisplay createVirtualDisplay(
+ @NonNull VirtualDisplayConfig config,
@Nullable Handler handler,
@Nullable VirtualDisplay.Callback callback) {
- if (!ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE && requestedRefreshRate != 0.0f) {
- Slog.e(TAG, "Please turn on ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE to use the new api");
- return null;
- }
-
- final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
- height, densityDpi);
- builder.setFlags(flags);
- if (surface != null) {
- builder.setSurface(surface);
- }
- if (requestedRefreshRate != 0.0f) {
- builder.setRequestedRefreshRate(requestedRefreshRate);
- }
- return createVirtualDisplay(null /* projection */, builder.build(), callback, handler,
+ return createVirtualDisplay(null /* projection */, config, callback, handler,
null /* windowContext */);
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index f6a2e33c7164..6b56a067a198 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,121 +18,44 @@ package android.hardware.display;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.media.projection.MediaProjection;
+import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Display;
import android.view.Surface;
-import com.android.internal.util.DataClass;
-
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
- * Holds configuration used to create {@link VirtualDisplay} instances. See
- * {@link MediaProjection#createVirtualDisplay} and
- * {@link android.companion.virtual.VirtualDeviceManager.VirtualDevice#createVirtualDisplay}.
+ * Holds configuration used to create {@link VirtualDisplay} instances.
*
- * @hide
+ * @see DisplayManager#createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback)
+ * @see MediaProjection#createVirtualDisplay
*/
-@DataClass(genParcelable = true, genAidl = true, genBuilder = true)
public final class VirtualDisplayConfig implements Parcelable {
- /**
- * The name of the virtual display, must be non-empty.
- */
- @NonNull
- private String mName;
-
- /**
- * The width of the virtual display in pixels. Must be greater than 0.
- */
- @IntRange(from = 1)
- private int mWidth;
-
- /**
- * The height of the virtual display in pixels. Must be greater than 0.
- */
- @IntRange(from = 1)
- private int mHeight;
-
- /**
- * The density of the virtual display in dpi. Must be greater than 0.
- */
- @IntRange(from = 1)
- private int mDensityDpi;
-
- /**
- * A combination of virtual display flags.
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
- * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
- */
- @VirtualDisplayFlag
- private int mFlags = 0;
-
- /**
- * The surface to which the content of the virtual display should be rendered, or null if
- * there is none initially.
- */
- @Nullable
- private Surface mSurface = null;
-
- /**
- * The unique identifier for the display. Shouldn't be displayed to the user.
- * @hide
- */
- @Nullable
- private String mUniqueId = null;
-
- /**
- * The id of the display that the virtual display should mirror, or
- * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
- */
- private int mDisplayIdToMirror = DEFAULT_DISPLAY;
-
- /**
- * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
- * if DisplayManager should record contents instead.
- */
- private boolean mWindowManagerMirroring = false;
-
- /**
- * The display categories. If set, only corresponding activities from the same category can be
- * shown on the display.
- */
- @DataClass.PluralOf("displayCategory")
- @NonNull private List<String> mDisplayCategories = new ArrayList<>();
-
- /**
- * The refresh rate of a virtual display in frames per second.
- * If this value is non-zero, this is the requested refresh rate to set.
- * If this value is zero, the system chooses a default refresh rate.
- */
- private float mRequestedRefreshRate = 0.0f;
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ VirtualDisplayConfig(
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mFlags;
+ private final Surface mSurface;
+ private final String mUniqueId;
+ private final int mDisplayIdToMirror;
+ private final boolean mWindowManagerMirroring;
+ private ArrayList<String> mDisplayCategories = null;
+ private final float mRequestedRefreshRate;
+
+ private VirtualDisplayConfig(
@NonNull String name,
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@@ -142,219 +65,200 @@ public final class VirtualDisplayConfig implements Parcelable {
@Nullable String uniqueId,
int displayIdToMirror,
boolean windowManagerMirroring,
- @NonNull List<String> displayCategories,
+ @NonNull ArrayList<String> displayCategories,
float requestedRefreshRate) {
- this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mName);
- this.mWidth = width;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mWidth,
- "from", 1);
- this.mHeight = height;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mHeight,
- "from", 1);
- this.mDensityDpi = densityDpi;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mDensityDpi,
- "from", 1);
- this.mFlags = flags;
- com.android.internal.util.AnnotationValidations.validate(
- VirtualDisplayFlag.class, null, mFlags);
- this.mSurface = surface;
- this.mUniqueId = uniqueId;
- this.mDisplayIdToMirror = displayIdToMirror;
- this.mWindowManagerMirroring = windowManagerMirroring;
- this.mDisplayCategories = displayCategories;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mDisplayCategories);
- this.mRequestedRefreshRate = requestedRefreshRate;
-
- // onConstructed(); // You can define this method to get a callback
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mFlags = flags;
+ mSurface = surface;
+ mUniqueId = uniqueId;
+ mDisplayIdToMirror = displayIdToMirror;
+ mWindowManagerMirroring = windowManagerMirroring;
+ mDisplayCategories = displayCategories;
+ mRequestedRefreshRate = requestedRefreshRate;
}
/**
- * The name of the virtual display, must be non-empty.
+ * Returns the name of the virtual display.
*/
- @DataClass.Generated.Member
- public @NonNull String getName() {
+ @NonNull
+ public String getName() {
return mName;
}
/**
- * The width of the virtual display in pixels. Must be greater than 0.
+ * Returns the width of the virtual display in pixels.
*/
- @DataClass.Generated.Member
- public @IntRange(from = 1) int getWidth() {
+ public int getWidth() {
return mWidth;
}
/**
- * The height of the virtual display in pixels. Must be greater than 0.
+ * Returns the height of the virtual display in pixels.
*/
- @DataClass.Generated.Member
- public @IntRange(from = 1) int getHeight() {
+ public int getHeight() {
return mHeight;
}
/**
- * The density of the virtual display in dpi. Must be greater than 0.
+ * Returns the density of the virtual display in dpi.
*/
- @DataClass.Generated.Member
- public @IntRange(from = 1) int getDensityDpi() {
+ public int getDensityDpi() {
return mDensityDpi;
}
/**
- * A combination of virtual display flags.
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
- * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
- * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
+ * Returns the virtual display flags.
+ *
+ * @see Builder#setFlags
*/
- @DataClass.Generated.Member
- public @VirtualDisplayFlag int getFlags() {
+ public int getFlags() {
return mFlags;
}
/**
- * The surface to which the content of the virtual display should be rendered, or null if
- * there is none initially.
+ * Returns the surface to which the content of the virtual display should be rendered, if any.
+ *
+ * @see Builder#setSurface
*/
- @DataClass.Generated.Member
- public @Nullable Surface getSurface() {
+ @Nullable
+ public Surface getSurface() {
return mSurface;
}
/**
- * The unique identifier for the display. Shouldn't be displayed to the user.
- *
+ * Returns the unique identifier for the display. Shouldn't be displayed to the user.
* @hide
*/
- @DataClass.Generated.Member
- public @Nullable String getUniqueId() {
+ @Nullable
+ public String getUniqueId() {
return mUniqueId;
}
/**
- * The id of the display that the virtual display should mirror, or
- * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
+ * Returns the id of the display that the virtual display should mirror, or
+ * {@link android.view.Display#DEFAULT_DISPLAY} if there is none.
+ * @hide
*/
- @DataClass.Generated.Member
public int getDisplayIdToMirror() {
return mDisplayIdToMirror;
}
/**
- * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
+ * Whether if WindowManager is responsible for mirroring content to this VirtualDisplay, or
* if DisplayManager should record contents instead.
+ * @hide
*/
- @DataClass.Generated.Member
public boolean isWindowManagerMirroring() {
return mWindowManagerMirroring;
}
/**
- * The display categories. If set, only corresponding activities from the same category can be
- * shown on the display.
+ * Returns the display categories.
+ *
+ * @see Builder#setDisplayCategories
*/
- @DataClass.Generated.Member
- public @NonNull List<String> getDisplayCategories() {
- return mDisplayCategories;
+ @NonNull
+ public List<String> getDisplayCategories() {
+ return Collections.unmodifiableList(mDisplayCategories);
}
/**
- * The refresh rate of a virtual display in frames per second.
- * If this value is none zero, this is the requested refresh rate to set.
- * If this value is zero, the system chooses a default refresh rate.
+ * Returns the refresh rate of a virtual display in frames per second, or zero if it is using a
+ * default refresh rate chosen by the system.
+ *
+ * @see Builder#setRequestedRefreshRate
*/
- @DataClass.Generated.Member
public float getRequestedRefreshRate() {
return mRequestedRefreshRate;
}
@Override
- @DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- int flg = 0;
- if (mWindowManagerMirroring) flg |= 0x100;
- if (mSurface != null) flg |= 0x20;
- if (mUniqueId != null) flg |= 0x40;
- dest.writeInt(flg);
- dest.writeString(mName);
+ dest.writeString8(mName);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
dest.writeInt(mDensityDpi);
dest.writeInt(mFlags);
- if (mSurface != null) dest.writeTypedObject(mSurface, flags);
- if (mUniqueId != null) dest.writeString(mUniqueId);
+ dest.writeTypedObject(mSurface, flags);
+ dest.writeString8(mUniqueId);
dest.writeInt(mDisplayIdToMirror);
+ dest.writeBoolean(mWindowManagerMirroring);
dest.writeStringList(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
}
@Override
- @DataClass.Generated.Member
public int describeContents() { return 0; }
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ VirtualDisplayConfig(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- int flg = in.readInt();
- boolean windowManagerMirroring = (flg & 0x100) != 0;
- String name = in.readString();
- int width = in.readInt();
- int height = in.readInt();
- int densityDpi = in.readInt();
- int flags = in.readInt();
- Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
- String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
- int displayIdToMirror = in.readInt();
- List<String> displayCategories = new ArrayList<>();
- in.readStringList(displayCategories);
- float requestedRefreshRate = in.readFloat();
-
- this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mName);
- this.mWidth = width;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mWidth,
- "from", 1);
- this.mHeight = height;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mHeight,
- "from", 1);
- this.mDensityDpi = densityDpi;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mDensityDpi,
- "from", 1);
- this.mFlags = flags;
- com.android.internal.util.AnnotationValidations.validate(
- VirtualDisplayFlag.class, null, mFlags);
- this.mSurface = surface;
- this.mUniqueId = uniqueId;
- this.mDisplayIdToMirror = displayIdToMirror;
- this.mWindowManagerMirroring = windowManagerMirroring;
- this.mDisplayCategories = displayCategories;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mDisplayCategories);
- this.mRequestedRefreshRate = requestedRefreshRate;
-
- // onConstructed(); // You can define this method to get a callback
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof VirtualDisplayConfig)) {
+ return false;
+ }
+ VirtualDisplayConfig that = (VirtualDisplayConfig) o;
+ return Objects.equals(mName, that.mName)
+ && mWidth == that.mWidth
+ && mHeight == that.mHeight
+ && mDensityDpi == that.mDensityDpi
+ && mFlags == that.mFlags
+ && Objects.equals(mSurface, that.mSurface)
+ && Objects.equals(mUniqueId, that.mUniqueId)
+ && mDisplayIdToMirror == that.mDisplayIdToMirror
+ && mWindowManagerMirroring == that.mWindowManagerMirroring
+ && Objects.equals(mDisplayCategories, that.mDisplayCategories)
+ && mRequestedRefreshRate == that.mRequestedRefreshRate;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hash(
+ mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
+ mDisplayIdToMirror, mWindowManagerMirroring, mDisplayCategories,
+ mRequestedRefreshRate);
+ return hashCode;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "VirtualDisplayConfig("
+ + " mName=" + mName
+ + " mHeight=" + mHeight
+ + " mWidth=" + mWidth
+ + " mDensityDpi=" + mDensityDpi
+ + " mFlags=" + mFlags
+ + " mSurface=" + mSurface
+ + " mUniqueId=" + mUniqueId
+ + " mDisplayIdToMirror=" + mDisplayIdToMirror
+ + " mWindowManagerMirroring=" + mWindowManagerMirroring
+ + " mDisplayCategories=" + mDisplayCategories
+ + " mRequestedRefreshRate=" + mRequestedRefreshRate
+ + ")";
+ }
+
+ private VirtualDisplayConfig(@NonNull Parcel in) {
+ mName = in.readString8();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mDensityDpi = in.readInt();
+ mFlags = in.readInt();
+ mSurface = in.readTypedObject(Surface.CREATOR);
+ mUniqueId = in.readString8();
+ mDisplayIdToMirror = in.readInt();
+ mWindowManagerMirroring = in.readBoolean();
+ mDisplayCategories = new ArrayList<>();
+ in.readStringList(mDisplayCategories);
+ mRequestedRefreshRate = in.readFloat();
}
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR
+ @NonNull
+ public static final Parcelable.Creator<VirtualDisplayConfig> CREATOR
= new Parcelable.Creator<VirtualDisplayConfig>() {
@Override
public VirtualDisplayConfig[] newArray(int size) {
@@ -368,229 +272,161 @@ public final class VirtualDisplayConfig implements Parcelable {
};
/**
- * A builder for {@link VirtualDisplayConfig}
+ * A builder for {@link VirtualDisplayConfig}.
*/
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
public static final class Builder {
-
- private @NonNull String mName;
- private @IntRange(from = 1) int mWidth;
- private @IntRange(from = 1) int mHeight;
- private @IntRange(from = 1) int mDensityDpi;
- private @VirtualDisplayFlag int mFlags;
- private @Nullable Surface mSurface;
- private @Nullable String mUniqueId;
- private int mDisplayIdToMirror;
- private boolean mWindowManagerMirroring;
- private @NonNull List<String> mDisplayCategories;
- private float mRequestedRefreshRate;
-
- private long mBuilderFieldsSet = 0L;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private int mFlags = 0;
+ private Surface mSurface = null;
+ private String mUniqueId = null;
+ private int mDisplayIdToMirror = DEFAULT_DISPLAY;
+ private boolean mWindowManagerMirroring = false;
+ private ArrayList<String> mDisplayCategories = new ArrayList<>();
+ private float mRequestedRefreshRate = 0.0f;
/**
* Creates a new Builder.
*
- * @param name
- * The name of the virtual display, must be non-empty.
- * @param width
- * The width of the virtual display in pixels. Must be greater than 0.
- * @param height
- * The height of the virtual display in pixels. Must be greater than 0.
- * @param densityDpi
- * The density of the virtual display in dpi. Must be greater than 0.
+ * @param name The name of the virtual display, must be non-empty.
+ * @param width The width of the virtual display in pixels. Must be greater than 0.
+ * @param height The height of the virtual display in pixels. Must be greater than 0.
+ * @param densityDpi The density of the virtual display in dpi. Must be greater than 0.
*/
public Builder(
@NonNull String name,
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@IntRange(from = 1) int densityDpi) {
+ if (name == null) {
+ throw new IllegalArgumentException("Virtual display name is required");
+ }
+ if (width <= 0) {
+ throw new IllegalArgumentException("Virtual display width must be positive");
+ }
+ if (height <= 0) {
+ throw new IllegalArgumentException("Virtual display height must be positive");
+ }
+ if (densityDpi <= 0) {
+ throw new IllegalArgumentException("Virtual display density must be positive");
+ }
mName = name;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mName);
mWidth = width;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mWidth,
- "from", 1);
mHeight = height;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mHeight,
- "from", 1);
mDensityDpi = densityDpi;
- com.android.internal.util.AnnotationValidations.validate(
- IntRange.class, null, mDensityDpi,
- "from", 1);
- }
-
- /**
- * The name of the virtual display, must be non-empty.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setName(@NonNull String value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x1;
- mName = value;
- return this;
- }
-
- /**
- * The width of the virtual display in pixels. Must be greater than 0.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setWidth(@IntRange(from = 1) int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x2;
- mWidth = value;
- return this;
- }
-
- /**
- * The height of the virtual display in pixels. Must be greater than 0.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setHeight(@IntRange(from = 1) int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mHeight = value;
- return this;
- }
-
- /**
- * The density of the virtual display in dpi. Must be greater than 0.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setDensityDpi(@IntRange(from = 1) int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8;
- mDensityDpi = value;
- return this;
}
/**
- * A combination of virtual display flags.
+ * Sets the virtual display flags, a combination of
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
* or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
*/
- @DataClass.Generated.Member
- public @NonNull Builder setFlags(@VirtualDisplayFlag int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x10;
- mFlags = value;
+ @NonNull
+ public Builder setFlags(@VirtualDisplayFlag int flags) {
+ mFlags = flags;
return this;
}
/**
- * The surface to which the content of the virtual display should be rendered, or null if
- * there is none initially.
+ * Sets the surface to which the content of the virtual display should be rendered.
+ *
+ * <p>The surface can also be set after the display creation using
+ * {@link VirtualDisplay#setSurface(Surface)}.
*/
- @DataClass.Generated.Member
- public @NonNull Builder setSurface(@NonNull Surface value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x20;
- mSurface = value;
+ @NonNull
+ public Builder setSurface(@Nullable Surface surface) {
+ mSurface = surface;
return this;
}
/**
- * The unique identifier for the display. Shouldn't be displayed to the user.
- *
+ * Sets the unique identifier for the display.
* @hide
*/
- @DataClass.Generated.Member
- public @NonNull Builder setUniqueId(@NonNull String value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x40;
- mUniqueId = value;
+ @NonNull
+ public Builder setUniqueId(@Nullable String uniqueId) {
+ mUniqueId = uniqueId;
return this;
}
/**
- * The id of the display that the virtual display should mirror, or
- * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
+ * Sets the id of the display that the virtual display should mirror.
+ * @hide
*/
- @DataClass.Generated.Member
- public @NonNull Builder setDisplayIdToMirror(int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x80;
- mDisplayIdToMirror = value;
+ @NonNull
+ public Builder setDisplayIdToMirror(int displayIdToMirror) {
+ mDisplayIdToMirror = displayIdToMirror;
return this;
}
/**
- * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
- * if DisplayManager should record contents instead.
+ * Sets whether WindowManager is responsible for mirroring content to this VirtualDisplay.
+ * If unset or false, DisplayManager should record contents instead.
+ * @hide
*/
- @DataClass.Generated.Member
- public @NonNull Builder setWindowManagerMirroring(boolean value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x100;
- mWindowManagerMirroring = value;
+ @NonNull
+ public Builder setWindowManagerMirroring(boolean windowManagerMirroring) {
+ mWindowManagerMirroring = windowManagerMirroring;
return this;
}
/**
- * The display categories. If set, only corresponding activities from the same category can be
- * shown on the display.
+ * Sets the display categories.
+ *
+ * <p>The categories of the display indicate the type of activities allowed to run on that
+ * display. Activities can declare a display category using
+ * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
*/
- @DataClass.Generated.Member
- public @NonNull Builder setDisplayCategories(@NonNull List<String> value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x200;
- mDisplayCategories = value;
+ @NonNull
+ public Builder setDisplayCategories(@NonNull List<String> displayCategories) {
+ mDisplayCategories.clear();
+ mDisplayCategories.addAll(Objects.requireNonNull(displayCategories));
return this;
}
- /** @see #setDisplayCategories */
- @DataClass.Generated.Member
- public @NonNull Builder addDisplayCategory(@NonNull String value) {
- if (mDisplayCategories == null) setDisplayCategories(new ArrayList<>());
- mDisplayCategories.add(value);
+ /**
+ * Adds a display category.
+ *
+ * @see #setDisplayCategories
+ */
+ @NonNull
+ public Builder addDisplayCategory(@NonNull String displayCategory) {
+ mDisplayCategories.add(Objects.requireNonNull(displayCategory));
return this;
}
/**
- * The refresh rate of a virtual display in frames per second.
- * If this value is none zero, this is the requested refresh rate to set.
- * If this value is zero, the system chooses a default refresh rate.
+ * Sets the refresh rate of a virtual display in frames per second.
+ *
+ * <p>For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
+ * a 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded up
+ * down to a divisor of the physical display. If unset or zero, the virtual display will be
+ * refreshed at the physical display refresh rate.
+ *
+ * @see Display#getRefreshRate()
*/
- @DataClass.Generated.Member
- public @NonNull Builder setRequestedRefreshRate(float value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x400;
- mRequestedRefreshRate = value;
+ @NonNull
+ public Builder setRequestedRefreshRate(
+ @FloatRange(from = 0.0f) float requestedRefreshRate) {
+ if (requestedRefreshRate < 0.0f) {
+ throw new IllegalArgumentException(
+ "Virtual display requested refresh rate must be non-negative");
+ }
+ mRequestedRefreshRate = requestedRefreshRate;
return this;
}
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull VirtualDisplayConfig build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x800; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x10) == 0) {
- mFlags = 0;
- }
- if ((mBuilderFieldsSet & 0x20) == 0) {
- mSurface = null;
- }
- if ((mBuilderFieldsSet & 0x40) == 0) {
- mUniqueId = null;
- }
- if ((mBuilderFieldsSet & 0x80) == 0) {
- mDisplayIdToMirror = DEFAULT_DISPLAY;
- }
- if ((mBuilderFieldsSet & 0x100) == 0) {
- mWindowManagerMirroring = false;
- }
- if ((mBuilderFieldsSet & 0x200) == 0) {
- mDisplayCategories = new ArrayList<>();
- }
- if ((mBuilderFieldsSet & 0x400) == 0) {
- mRequestedRefreshRate = 0.0f;
- }
- VirtualDisplayConfig o = new VirtualDisplayConfig(
+ /**
+ * Builds the {@link VirtualDisplayConfig} instance.
+ */
+ @NonNull
+ public VirtualDisplayConfig build() {
+ return new VirtualDisplayConfig(
mName,
mWidth,
mHeight,
@@ -602,27 +438,6 @@ public final class VirtualDisplayConfig implements Parcelable {
mWindowManagerMirroring,
mDisplayCategories,
mRequestedRefreshRate);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x800) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
}
}
-
- @DataClass.Generated(
- time = 1671047069703L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate boolean mWindowManagerMirroring\nprivate @com.android.internal.util.DataClass.PluralOf(\"displayCategory\") @android.annotation.NonNull java.util.List<java.lang.String> mDisplayCategories\nprivate float mRequestedRefreshRate\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
}
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 4009fa7682fe..1c6de0464245 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -52,7 +52,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
}
/** The sensor id for this operation. */
- private final int mSensorId;
+ private int mSensorId;
private static int defaultSensorId() {
return -1;
}
@@ -299,6 +299,15 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
}
/**
+ * The sensor id for this operation.
+ */
+ @DataClass.Generated.Member
+ public @NonNull FaceAuthenticateOptions setSensorId( int value) {
+ mSensorId = value;
+ return this;
+ }
+
+ /**
* The package name for that operation that should be used for
* {@link android.app.AppOpsManager} verification.
*
@@ -610,10 +619,10 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable
}
@DataClass.Generated(
- time = 1676508211385L,
+ time = 1677119626034L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java",
- inputSignatures = "private final int mUserId\nprivate final int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 2857627bf712..9d5073e43957 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -68,7 +68,7 @@ interface IFaceService {
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
- void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token,
+ void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, IBiometricSensorReceiver sensorReceiver,
in FaceAuthenticateOptions options, long requestId, int cookie,
boolean allowBackgroundAuthentication);
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index cecb3172a8d1..763246e25a26 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -46,7 +46,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
}
/** The sensor id for this operation. */
- private final int mSensorId;
+ private int mSensorId;
private static int defaultSensorId() {
return SENSOR_ID_ANY;
}
@@ -176,6 +176,15 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
}
/**
+ * The sensor id for this operation.
+ */
+ @DataClass.Generated.Member
+ public @NonNull FingerprintAuthenticateOptions setSensorId( int value) {
+ mSensorId = value;
+ return this;
+ }
+
+ /**
* The package name for that operation that should be used for
* {@link android.app.AppOpsManager} verification.
*
@@ -433,10 +442,10 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions
}
@DataClass.Generated(
- time = 1676508212083L,
+ time = 1677119626721L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
- inputSignatures = "private final int mUserId\nprivate final int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index e3ae299be730..ec5749ed4f05 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -73,8 +73,8 @@ interface IFingerprintService {
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
@EnforcePermission("MANAGE_BIOMETRIC")
- void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
- IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+ void prepareForAuthentication(IBinder token, long operationId,
+ IBiometricSensorReceiver sensorReceiver, in FingerprintAuthenticateOptions options, long requestId,
int cookie, boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
diff --git a/core/java/android/os/CancellationSignalBeamer.java b/core/java/android/os/CancellationSignalBeamer.java
new file mode 100644
index 000000000000..afb5ff7bf626
--- /dev/null
+++ b/core/java/android/os/CancellationSignalBeamer.java
@@ -0,0 +1,325 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.system.SystemCleaner;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.util.HashMap;
+
+/**
+ * A transport for {@link CancellationSignal}, but unlike
+ * {@link CancellationSignal#createTransport()} doesn't require pre-creating the transport in the
+ * target process. Instead, cancellation is forwarded over the same IPC surface as the cancellable
+ * request.
+ *
+ * <p><strong>Important:</strong> For this to work, the following invariants must be held up:
+ * <ul>
+ * <li>A call to beam() <strong>MUST</strong> result in a call to close() on the result
+ * (otherwise, the token will be leaked and cancellation isn't propagated), and that call
+ * must happen after the call using the
+ * token is sent (otherwise, any concurrent cancellation may be lost). It is strongly
+ * recommended to use try-with-resources on the token.
+ * <li>The cancel(), forget() and cancellable operations transporting the token must either
+ * all be oneway on the same binder, or all be non-oneway to guarantee proper ordering.
+ * <li>A {@link CancellationSignal} <strong>SHOULD</strong> be used only once, as there
+ * can only be a single {@link android.os.CancellationSignal.OnCancelListener OnCancelListener}.
+ *
+ * </ul>
+ * <p>Caveats:
+ * <ul>
+ * <li>Cancellation is only ever dispatched after the token is closed, and thus after the
+ * call performing the cancellable operation (if the invariants are followed). The operation
+ * must therefore not block the incoming binder thread, or cancellation won't be possible.
+ * <li>Consequently, in the unlikely event that the sender dies right after beaming an already
+ * cancelled {@link CancellationSignal}, the cancellation may be lost (unlike with
+ * {@link CancellationSignal#createTransport()}).
+ * <li>The forwarding OnCancelListener is set in the implied finally phase of try-with-resources
+ * / when closing the token. If the receiver is in the same process, and the signal is
+ * already cancelled, this may invoke the target's OnCancelListener during that phase.
+ * </ul>
+ *
+ *
+ * <p>Usage:
+ * <pre>
+ * // Sender:
+ *
+ * class FooManager {
+ * var mCancellationSignalSender = new CancellationSignalBeamer.Sender() {
+ * &#064;Override
+ * public void onCancel(IBinder token) { remoteIFooService.onCancelToken(token); }
+ *
+ * &#064;Override
+ * public void onForget(IBinder token) { remoteIFooService.onForgetToken(token); }
+ * };
+ *
+ * public void doCancellableOperation(..., CancellationSignal cs) {
+ * try (var csToken = mCancellationSignalSender.beam(cs)) {
+ * remoteIFooService.doCancellableOperation(..., csToken);
+ * }
+ * }
+ * }
+ *
+ * // Receiver:
+ *
+ * class FooManagerService extends IFooService.Stub {
+ * var mCancellationSignalReceiver = new CancellationSignalBeamer.Receiver();
+ *
+ * &#064;Override
+ * public void doCancellableOperation(..., IBinder csToken) {
+ * CancellationSignal cs = mCancellationSignalReceiver.unbeam(csToken))
+ * // ...
+ * }
+ *
+ * &#064;Override
+ * public void onCancelToken(..., IBinder csToken) {
+ * mCancellationSignalReceiver.cancelToken(csToken))
+ * }
+ *
+ * &#064;Override
+ * public void onForgetToken(..., IBinder csToken) {
+ * mCancellationSignalReceiver.forgetToken(csToken))
+ * }
+ * }
+ *
+ * </pre>
+ *
+ * @hide
+ */
+public class CancellationSignalBeamer {
+
+ static final Cleaner sCleaner = SystemCleaner.cleaner();
+
+ /** The sending side of an {@link CancellationSignalBeamer} */
+ public abstract static class Sender {
+
+ /**
+ * Beams a {@link CancellationSignal} through an existing Binder interface.
+ *
+ * @param cs the {@code CancellationSignal} to beam, or {@code null}.
+ * @return an {@link IBinder} token. MUST be {@link CloseableToken#close}d <em>after</em>
+ * the binder call transporting it to the remote process, best with
+ * try-with-resources. {@code null} if {@code cs} was {@code null}.
+ */
+ // TODO(b/254888024): @MustBeClosed
+ @Nullable
+ public CloseableToken beam(@Nullable CancellationSignal cs) {
+ if (cs == null) {
+ return null;
+ }
+ return new Token(this, cs);
+ }
+
+ /**
+ * A {@link #beam}ed {@link CancellationSignal} was closed.
+ *
+ * MUST be forwarded to {@link Receiver#cancel} with proper ordering. See
+ * {@link CancellationSignalBeamer} for details.
+ */
+ public abstract void onCancel(IBinder token);
+
+ /**
+ * A {@link #beam}ed {@link CancellationSignal} was GC'd.
+ *
+ * MUST be forwarded to {@link Receiver#forget} with proper ordering. See
+ * {@link CancellationSignalBeamer} for details.
+ */
+ public abstract void onForget(IBinder token);
+
+ private static class Token extends Binder implements CloseableToken, Runnable {
+
+ private final Sender mSender;
+ private Preparer mPreparer;
+
+ private Token(Sender sender, CancellationSignal signal) {
+ mSender = sender;
+ mPreparer = new Preparer(sender, signal, this);
+ }
+
+ @Override
+ public void close() {
+ Preparer preparer = mPreparer;
+ mPreparer = null;
+ if (preparer != null) {
+ preparer.setup();
+ }
+ }
+
+ @Override
+ public void run() {
+ mSender.onForget(this);
+ }
+
+ private static class Preparer implements CancellationSignal.OnCancelListener {
+ private final Sender mSender;
+ private final CancellationSignal mSignal;
+ private final Token mToken;
+
+ private Preparer(Sender sender, CancellationSignal signal, Token token) {
+ mSender = sender;
+ mSignal = signal;
+ mToken = token;
+ }
+
+ void setup() {
+ sCleaner.register(this, mToken);
+ mSignal.setOnCancelListener(this);
+ }
+
+ @Override
+ public void onCancel() {
+ try {
+ mSender.onCancel(mToken);
+ } finally {
+ // Make sure we dispatch onCancel before the cleaner can run.
+ Reference.reachabilityFence(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * A {@link #beam}ed {@link CancellationSignal} ready for sending over Binder.
+ *
+ * MUST be closed <em>after</em> it is sent over binder, ideally through try-with-resources.
+ */
+ public interface CloseableToken extends IBinder, AutoCloseable {
+ @Override
+ void close(); // No throws
+ }
+ }
+
+ /** The receiving side of a {@link CancellationSignalBeamer}. */
+ public static class Receiver implements IBinder.DeathRecipient {
+ private final HashMap<IBinder, CancellationSignal> mTokenMap = new HashMap<>();
+ private final boolean mCancelOnSenderDeath;
+
+ /**
+ * Constructs a new {@code Receiver}.
+ *
+ * @param cancelOnSenderDeath if true, {@link CancellationSignal}s obtained from
+ * {@link #unbeam} are automatically {@link #cancel}led if the sender token
+ * {@link Binder#linkToDeath dies}; otherwise they are simnply dropped. Note: if the
+ * sending process drops all references to the {@link CancellationSignal} before
+ * process death, the cancellation is not guaranteed.
+ */
+ public Receiver(boolean cancelOnSenderDeath) {
+ mCancelOnSenderDeath = cancelOnSenderDeath;
+ }
+
+ /**
+ * Unbeams a token that was obtained via {@link Sender#beam} and turns it back into a
+ * {@link CancellationSignal}.
+ *
+ * A subsequent call to {@link #cancel} with the same token will cancel the returned
+ * {@code CancellationSignal}.
+ *
+ * @param token a token that was obtained from {@link Sender}, possibly in a remote process.
+ * @return a {@link CancellationSignal} linked to the given token.
+ */
+ @Nullable
+ public CancellationSignal unbeam(@Nullable IBinder token) {
+ if (token == null) {
+ return null;
+ }
+ synchronized (this) {
+ CancellationSignal cs = mTokenMap.get(token);
+ if (cs != null) {
+ return cs;
+ }
+
+ cs = new CancellationSignal();
+ mTokenMap.put(token, cs);
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ dead(token);
+ }
+ return cs;
+ }
+ }
+
+ /**
+ * Forgets state associated with the given token (if any).
+ *
+ * Subsequent calls to {@link #cancel} or binder death notifications on the token will not
+ * have any effect.
+ *
+ * This MUST be invoked when forwarding {@link Sender#onForget}, otherwise the token and
+ * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+ *
+ * Optionally, the receiving service logic may also invoke this if it can guarantee that
+ * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+ * using the CancellationSignal has been fully completed).
+ *
+ * @param token the token to forget. No-op if {@code null}.
+ */
+ public void forget(@Nullable IBinder token) {
+ synchronized (this) {
+ if (mTokenMap.remove(token) != null) {
+ token.unlinkToDeath(this, 0);
+ }
+ }
+ }
+
+ /**
+ * Cancels the {@link CancellationSignal} associated with the given token (if any).
+ *
+ * This MUST be invoked when forwarding {@link Sender#onCancel}, otherwise the token and
+ * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+ *
+ * Optionally, the receiving service logic may also invoke this if it can guarantee that
+ * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+ * using the CancellationSignal has been fully completed).
+ *
+ * @param token the token to forget. No-op if {@code null}.
+ */
+ public void cancel(@Nullable IBinder token) {
+ CancellationSignal cs;
+ synchronized (this) {
+ cs = mTokenMap.get(token);
+ if (cs != null) {
+ forget(token);
+ } else {
+ return;
+ }
+ }
+ cs.cancel();
+ }
+
+ private void dead(@NonNull IBinder token) {
+ if (mCancelOnSenderDeath) {
+ cancel(token);
+ } else {
+ forget(token);
+ }
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder who) {
+ dead(who);
+ }
+
+ @Override
+ public void binderDied() {
+ throw new RuntimeException("unreachable");
+ }
+ }
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index de94b867382e..8d8deaa28df1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3057,6 +3057,7 @@ public class UserManager {
*
* @hide
*/
+ @TestApi
public int getDisplayIdAssignedToUser() {
try {
return mService.getDisplayIdAssignedToUser();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index acb1f5bede9b..a3f859909ec5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -99,7 +99,6 @@ import android.widget.Editor;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import com.android.internal.widget.ILockSettings;
import java.io.IOException;
import java.lang.annotation.ElementType;
@@ -3218,13 +3217,13 @@ public final class Settings {
}
}
} else {
- if (LOCAL_LOGV) {
+ if (DEBUG || LOCAL_LOGV) {
Log.v(TAG, "get setting for user " + userHandle
+ " by user " + UserHandle.myUserId() + " so skipping cache");
}
}
if (DEBUG) {
- Log.i(TAG, "Cache miss for setting:" + name);
+ Log.i(TAG, "Cache miss for setting:" + name + " for user:" + userHandle);
}
// Check if the target settings key is readable. Reject if the caller is not system and
@@ -6192,9 +6191,6 @@ public final class Settings {
sProviderHolder,
Secure.class);
- private static ILockSettings sLockSettings = null;
-
- private static boolean sIsSystemProcess;
@UnsupportedAppUsage
private static final HashSet<String> MOVED_TO_LOCK_SETTINGS;
@UnsupportedAppUsage
@@ -6358,35 +6354,25 @@ public final class Settings {
return Global.getStringForUser(resolver, name, userHandle);
}
- if (MOVED_TO_LOCK_SETTINGS.contains(name)) {
- synchronized (Secure.class) {
- if (sLockSettings == null) {
- sLockSettings = ILockSettings.Stub.asInterface(
- (IBinder) ServiceManager.getService("lock_settings"));
- sIsSystemProcess = Process.myUid() == Process.SYSTEM_UID;
- }
- }
- if (sLockSettings != null && !sIsSystemProcess) {
- // No context; use the ActivityThread's context as an approximation for
- // determining the target API level.
- Application application = ActivityThread.currentApplication();
-
- boolean isPreMnc = application != null
- && application.getApplicationInfo() != null
- && application.getApplicationInfo().targetSdkVersion
- <= VERSION_CODES.LOLLIPOP_MR1;
- if (isPreMnc) {
- try {
- return sLockSettings.getString(name, "0", userHandle);
- } catch (RemoteException re) {
- // Fall through
- }
- } else {
- throw new SecurityException("Settings.Secure." + name
- + " is deprecated and no longer accessible."
- + " See API documentation for potential replacements.");
- }
+ if (MOVED_TO_LOCK_SETTINGS.contains(name) && Process.myUid() != Process.SYSTEM_UID) {
+ // No context; use the ActivityThread's context as an approximation for
+ // determining the target API level.
+ Application application = ActivityThread.currentApplication();
+
+ boolean isPreMnc = application != null
+ && application.getApplicationInfo() != null
+ && application.getApplicationInfo().targetSdkVersion
+ <= VERSION_CODES.LOLLIPOP_MR1;
+ if (isPreMnc) {
+ // Old apps used to get the three deprecated LOCK_PATTERN_* settings from
+ // ILockSettings.getString(). For security reasons, we now just return a
+ // stubbed-out value. Note: the only one of these three settings actually known
+ // to have been used was LOCK_PATTERN_ENABLED, and ILockSettings.getString()
+ // already always returned "0" for that starting in Android 11.
+ return "0";
}
+ throw new SecurityException("Settings.Secure." + name + " is deprecated and no" +
+ " longer accessible. See API documentation for potential replacements.");
}
return sNameValueCache.getStringForUser(resolver, name, userHandle);
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 6896e0244791..d14abfd5d7ae 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4937,4 +4937,66 @@ public final class Telephony {
return ALL_COLUMNS;
}
}
+
+ /**
+ * Stores incoming satellite datagrams.
+ * @hide
+ */
+ public static final class SatelliteDatagrams {
+ /**
+ * Not instantiable.
+ * @hide
+ */
+ private SatelliteDatagrams() {}
+
+ /**
+ * Provider name for Satellite Datagrams table.
+ */
+ public static final String PROVIDER_NAME = "satellite";
+
+ /**
+ * Table name for Satellite Datagrams table.
+ */
+ public static final String TABLE_NAME = "incoming_datagrams";
+
+ /**
+ * URL for satellite incoming datagrams table.
+ */
+ private static final String URL = "content://" + PROVIDER_NAME + "/" + TABLE_NAME;
+
+ /**
+ * The {@code content://} style URI for this provider.
+ * @hide
+ */
+ public static final Uri CONTENT_URI = Uri.parse(URL);
+
+ /**
+ * SatelliteProvider unique key column name is the datagram id.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String COLUMN_UNIQUE_KEY_DATAGRAM_ID = "datagram_id";
+
+ /**
+ * SatelliteProvider column name for storing datagram.
+ * <p>TYPE: BLOB
+ * @hide
+ */
+ public static final String COLUMN_DATAGRAM = "datagram";
+
+ /** All columns in {@link SatelliteDatagrams} table. */
+ private static final List<String> ALL_COLUMNS = List.of(
+ COLUMN_UNIQUE_KEY_DATAGRAM_ID,
+ COLUMN_DATAGRAM
+ );
+
+ /**
+ * @return All columns in {@link SatelliteDatagrams} table.
+ * @hide
+ */
+ @NonNull
+ public static List<String> getAllColumns() {
+ return ALL_COLUMNS;
+ }
+ }
}
diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java
index 81b9f22ebc7f..1ad0424f44e3 100644
--- a/core/java/android/service/credentials/BeginGetCredentialOption.java
+++ b/core/java/android/service/credentials/BeginGetCredentialOption.java
@@ -116,12 +116,12 @@ public class BeginGetCredentialOption implements Parcelable {
* @param id the unique id associated with this option
* @param type the requested credential type
* @param candidateQueryData the request candidateQueryData
- * @throws IllegalArgumentException If type is empty.
+ * @throws IllegalArgumentException If id or type is empty.
*/
public BeginGetCredentialOption(
@NonNull String id, @NonNull String type,
@NonNull Bundle candidateQueryData) {
- mId = id;
+ mId = Preconditions.checkStringNotEmpty(id, "id must not be empty");
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
Bundle bundle = new Bundle();
bundle.putAll(candidateQueryData);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 12cd5236017c..f53abce1d1ea 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2198,6 +2198,11 @@ public abstract class WallpaperService extends Service {
}
mCreated = false;
}
+
+ if (mSurfaceControl != null) {
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
}
private final DisplayListener mDisplayListener = new DisplayListener() {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index f648ad4fbac3..434b1c76113f 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -53,9 +53,7 @@ import com.android.internal.telephony.ITelephonyRegistry;
import java.lang.ref.WeakReference;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
@@ -83,15 +81,16 @@ public class TelephonyRegistryManager {
* A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and
* its callback IOnSubscriptionsChangedListener.
*/
- private final Map<SubscriptionManager.OnSubscriptionsChangedListener,
- IOnSubscriptionsChangedListener> mSubscriptionChangedListenerMap = new HashMap<>();
+ private final ConcurrentHashMap<SubscriptionManager.OnSubscriptionsChangedListener,
+ IOnSubscriptionsChangedListener>
+ mSubscriptionChangedListenerMap = new ConcurrentHashMap<>();
/**
* A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and
* its callback IOnSubscriptionsChangedListener.
*/
- private final Map<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener,
- IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap
- = new HashMap<>();
+ private final ConcurrentHashMap<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener,
+ IOnSubscriptionsChangedListener>
+ mOpportunisticSubscriptionChangedListenerMap = new ConcurrentHashMap<>();
/**
* A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 98c0d7f4fbf1..5aa0f59024f8 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -477,7 +477,14 @@ public abstract class NtpTrustedTime implements TrustedTime {
return mTimeResult;
}
- /** Clears the last received NTP. Intended for use during tests. */
+ /** Sets the last received NTP time. Intended for use during tests. */
+ public void setCachedTimeResult(TimeResult timeResult) {
+ synchronized (this) {
+ mTimeResult = timeResult;
+ }
+ }
+
+ /** Clears the last received NTP time. Intended for use during tests. */
public void clearCachedTimeResult() {
synchronized (this) {
mTimeResult = null;
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index 1a2c4df96b36..b4e1f59874b0 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -90,6 +90,14 @@ public class SparseArrayMap<K, V> {
}
/**
+ * Removes the data for the keyIndex and mapIndex, if there was any.
+ * @hide
+ */
+ public void deleteAt(int keyIndex, int mapIndex) {
+ mData.valueAt(keyIndex).removeAt(mapIndex);
+ }
+
+ /**
* Get the value associated with the int-K pair.
*/
@Nullable
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 20be9d65ab8e..547608899259 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1261,20 +1261,17 @@ public final class Display {
/**
* @hide
- * Returns the display's HDR supported types.
+ * Returns the current mode's supported HDR types.
*
* @see #isHdr()
- * @see HdrCapabilities#getSupportedHdrTypes()
+ * @see Mode#getSupportedHdrTypes()
*/
@TestApi
@NonNull
public int[] getReportedHdrTypes() {
synchronized (mLock) {
updateDisplayInfoLocked();
- if (mDisplayInfo.hdrCapabilities == null) {
- return new int[0];
- }
- return mDisplayInfo.hdrCapabilities.getSupportedHdrTypes();
+ return mDisplayInfo.getMode().getSupportedHdrTypes();
}
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index e02e600f53c8..39477380e196 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -20,7 +20,6 @@ import static android.view.InsetsSourceProto.FRAME;
import static android.view.InsetsSourceProto.TYPE;
import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.ime;
import android.annotation.IntRange;
@@ -169,7 +168,7 @@ public class InsetsSource implements Parcelable {
// During drag-move and drag-resizing, the caption insets position may not get updated
// before the app frame get updated. To layout the app content correctly during drag events,
// we always return the insets with the corresponding height covering the top.
- if (!CAPTION_ON_SHELL && getType() == WindowInsets.Type.captionBar()) {
+ if (getType() == WindowInsets.Type.captionBar()) {
return Insets.of(0, frame.height(), 0, 0);
}
// Checks for whether there is shared edge with insets for 0-width/height window.
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 9e1762065367..b574ecf32dd8 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -31,6 +31,7 @@ per-file Input*.java = file:/services/core/java/com/android/server/input/OWNERS
per-file Input*.aidl = file:/services/core/java/com/android/server/input/OWNERS
per-file KeyEvent.java = file:/services/core/java/com/android/server/input/OWNERS
per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS
+per-file MotionPredictor.java = file:/services/core/java/com/android/server/input/OWNERS
per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS
per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS
per-file BatchedInputEventReceiver.java = file:/services/core/java/com/android/server/input/OWNERS
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f430ec300b5b..2ff9ad346a71 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -11382,6 +11382,10 @@ public final class ViewRootImpl implements ViewParent,
sendBackKeyEvent(KeyEvent.ACTION_DOWN);
sendBackKeyEvent(KeyEvent.ACTION_UP);
};
+ if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) {
+ Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher");
+ return;
+ }
mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index aa631cfa5980..9504852f6e98 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -2068,12 +2068,14 @@ public final class AccessibilityManager {
* {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed.
*
* @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission.
+ * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
+ * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
*
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY,
+ Manifest.permission.CREATE_VIRTUAL_DEVICE})
public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) {
final IAccessibilityManager service;
synchronized (mLock) {
@@ -2096,12 +2098,14 @@ public final class AccessibilityManager {
* @return {@code true} if the proxy is successfully unregistered.
*
* @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission.
+ * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
+ * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
*
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY,
+ Manifest.permission.CREATE_VIRTUAL_DEVICE})
public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) {
final IAccessibilityManager service;
synchronized (mLock) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bdc7333f2751..aef0e651ff8d 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -899,9 +899,10 @@ public final class AutofillManager {
// 3. Get the activity names substring between the indexes
final int activityStringStartIndex = packageInStringIndex + packageName.length() + 1;
- if (activityStringStartIndex < firstNextSemicolonIndex) {
+ if (activityStringStartIndex >= firstNextSemicolonIndex) {
Log.e(TAG, "Failed to get denied activity names from denylist because it's wrongly "
+ "formatted");
+ return;
}
final String activitySubstring =
denyListString.substring(activityStringStartIndex, firstNextSemicolonIndex);
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index db17a533c303..d84acc03826b 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -25,6 +25,7 @@ import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -33,6 +34,7 @@ import android.view.WindowManager;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -40,7 +42,6 @@ import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.view.IImeTracker;
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
@@ -581,51 +582,57 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
@AnyThread
- @Nullable
- static IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
- @SoftInputShowHideReason int reason) {
+ @NonNull
+ static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
- return null;
+ // Create token with "fake" binder if the service was not found.
+ return new ImeTracker.Token(new Binder(), tag);
}
try {
- return service.onRequestShow(uid, origin, reason);
+ return service.onRequestShow(tag, uid, origin, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
@AnyThread
- @Nullable
- static IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
- @SoftInputShowHideReason int reason) {
+ @NonNull
+ static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
- return null;
+ // Create token with "fake" binder if the service was not found.
+ return new ImeTracker.Token(new Binder(), tag);
}
try {
- return service.onRequestHide(uid, origin, reason);
+ return service.onRequestHide(tag, uid, origin, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onProgress */
@AnyThread
- static void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+ static void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
return;
}
try {
- service.onProgress(statsToken, phase);
+ service.onProgress(binder, phase);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onFailed */
@AnyThread
- static void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+ static void onFailed(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
return;
@@ -637,8 +644,9 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onCancelled */
@AnyThread
- static void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+ static void onCancelled(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
return;
@@ -650,8 +658,9 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onShown */
@AnyThread
- static void onShown(@NonNull IBinder statsToken) {
+ static void onShown(@NonNull ImeTracker.Token statsToken) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
return;
@@ -663,8 +672,9 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onHidden */
@AnyThread
- static void onHidden(@NonNull IBinder statsToken) {
+ static void onHidden(@NonNull ImeTracker.Token statsToken) {
final IImeTracker service = getImeTrackerService();
if (service == null) {
return;
@@ -676,6 +686,7 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
@AnyThread
@RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
static boolean hasPendingImeVisibilityRequests() {
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index e5a99ff8566b..f0d1019ffb06 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -26,7 +26,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -47,7 +46,7 @@ import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
-import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
/** @hide */
@@ -321,9 +320,8 @@ public interface ImeTracker {
/**
* Creates an IME show request tracking token.
*
- * @param component the component name where the IME show request was created,
- * or {@code null} otherwise
- * (defaulting to {@link ActivityThread#currentProcessName()}).
+ * @param component the name of the component that created the IME request, or {@code null}
+ * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created.
@@ -337,9 +335,8 @@ public interface ImeTracker {
/**
* Creates an IME hide request tracking token.
*
- * @param component the component name where the IME hide request was created,
- * or {@code null} otherwise
- * (defaulting to {@link ActivityThread#currentProcessName()}).
+ * @param component the name of the component that created the IME request, or {@code null}
+ * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created.
@@ -435,8 +432,7 @@ public interface ImeTracker {
mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false);
// Update logging flag dynamically.
SystemProperties.addChangeCallback(() ->
- mLogProgress =
- SystemProperties.getBoolean("persist.debug.imetracker", false));
+ mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false));
}
/** Whether progress should be logged. */
@@ -446,10 +442,9 @@ public interface ImeTracker {
@Override
public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
@SoftInputShowHideReason int reason) {
- IBinder binder = IInputMethodManagerGlobalInvoker.onRequestShow(uid, origin, reason);
- if (binder == null) binder = new Binder();
-
- final Token token = Token.build(binder, component);
+ final var tag = getTag(component);
+ final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
+ reason);
Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -461,10 +456,9 @@ public interface ImeTracker {
@Override
public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
@SoftInputShowHideReason int reason) {
- IBinder binder = IInputMethodManagerGlobalInvoker.onRequestHide(uid, origin, reason);
- if (binder == null) binder = new Binder();
-
- final Token token = Token.build(binder, component);
+ final var tag = getTag(component);
+ final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
+ reason);
Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -485,7 +479,7 @@ public interface ImeTracker {
@Override
public void onFailed(@Nullable Token token, @Phase int phase) {
if (token == null) return;
- IInputMethodManagerGlobalInvoker.onFailed(token.mBinder, phase);
+ IInputMethodManagerGlobalInvoker.onFailed(token, phase);
Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
}
@@ -499,7 +493,7 @@ public interface ImeTracker {
@Override
public void onCancelled(@Nullable Token token, @Phase int phase) {
if (token == null) return;
- IInputMethodManagerGlobalInvoker.onCancelled(token.mBinder, phase);
+ IInputMethodManagerGlobalInvoker.onCancelled(token, phase);
Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
}
@@ -507,7 +501,7 @@ public interface ImeTracker {
@Override
public void onShown(@Nullable Token token) {
if (token == null) return;
- IInputMethodManagerGlobalInvoker.onShown(token.mBinder);
+ IInputMethodManagerGlobalInvoker.onShown(token);
Log.i(TAG, token.mTag + ": onShown");
}
@@ -515,10 +509,24 @@ public interface ImeTracker {
@Override
public void onHidden(@Nullable Token token) {
if (token == null) return;
- IInputMethodManagerGlobalInvoker.onHidden(token.mBinder);
+ IInputMethodManagerGlobalInvoker.onHidden(token);
Log.i(TAG, token.mTag + ": onHidden");
}
+
+ /**
+ * Returns a logging tag using the given component name.
+ *
+ * @param component the name of the component that created the IME request, or {@code null}
+ * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
+ */
+ @NonNull
+ private String getTag(@Nullable String component) {
+ if (component == null) {
+ component = ActivityThread.currentProcessName();
+ }
+ return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
+ }
};
/** The singleton IME tracker instance for instrumenting jank metrics. */
@@ -528,28 +536,31 @@ public interface ImeTracker {
ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker();
/** A token that tracks the progress of an IME request. */
- class Token implements Parcelable {
+ final class Token implements Parcelable {
+ /** The binder used to identify this token. */
@NonNull
- public final IBinder mBinder;
+ private final IBinder mBinder;
+ /** Logging tag, of the shape "component:random_hexadecimal". */
@NonNull
private final String mTag;
- @NonNull
- private static Token build(@NonNull IBinder binder, @Nullable String component) {
- if (component == null) component = ActivityThread.currentProcessName();
- final String tag = component + ":" + Integer.toHexString((new Random().nextInt()));
+ public Token(@NonNull IBinder binder, @NonNull String tag) {
+ mBinder = binder;
+ mTag = tag;
+ }
- return new Token(binder, tag);
+ private Token(@NonNull Parcel in) {
+ mBinder = in.readStrongBinder();
+ mTag = in.readString8();
}
- private Token(@NonNull IBinder binder, @NonNull String tag) {
- mBinder = binder;
- mTag = tag;
+ @NonNull
+ public IBinder getBinder() {
+ return mBinder;
}
- /** Returns the {@link Token#mTag} */
@NonNull
public String getTag() {
return mTag;
@@ -562,7 +573,7 @@ public interface ImeTracker {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mBinder);
dest.writeString8(mTag);
}
@@ -571,12 +582,11 @@ public interface ImeTracker {
public static final Creator<Token> CREATOR = new Creator<>() {
@NonNull
@Override
- public Token createFromParcel(Parcel source) {
- final IBinder binder = source.readStrongBinder();
- final String tag = source.readString8();
- return new Token(binder, tag);
+ public Token createFromParcel(@NonNull Parcel in) {
+ return new Token(in);
}
+ @NonNull
@Override
public Token[] newArray(int size) {
return new Token[size];
@@ -589,40 +599,50 @@ public interface ImeTracker {
*
* Note: This is held in a separate class so that it only gets initialized when actually needed.
*/
- class Debug {
+ final class Debug {
+ @NonNull
private static final Map<Integer, String> sTypes =
getFieldMapping(ImeTracker.class, "TYPE_");
+ @NonNull
private static final Map<Integer, String> sStatus =
getFieldMapping(ImeTracker.class, "STATUS_");
+ @NonNull
private static final Map<Integer, String> sOrigins =
getFieldMapping(ImeTracker.class, "ORIGIN_");
+ @NonNull
private static final Map<Integer, String> sPhases =
getFieldMapping(ImeTracker.class, "PHASE_");
+ @NonNull
public static String typeToString(@Type int type) {
return sTypes.getOrDefault(type, "TYPE_" + type);
}
+ @NonNull
public static String statusToString(@Status int status) {
return sStatus.getOrDefault(status, "STATUS_" + status);
}
+ @NonNull
public static String originToString(@Origin int origin) {
return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
}
+ @NonNull
public static String phaseToString(@Phase int phase) {
return sPhases.getOrDefault(phase, "PHASE_" + phase);
}
- private static Map<Integer, String> getFieldMapping(Class<?> cls, String fieldPrefix) {
+ @NonNull
+ private static Map<Integer, String> getFieldMapping(Class<?> cls,
+ @NonNull String fieldPrefix) {
return Arrays.stream(cls.getDeclaredFields())
.filter(field -> field.getName().startsWith(fieldPrefix))
.collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
}
- private static int getFieldValue(Field field) {
+ private static int getFieldValue(@NonNull Field field) {
try {
return field.getInt(null);
} catch (IllegalAccessException e) {
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index ec1badb637e4..8b55494c75d4 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -18,6 +18,7 @@ package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -83,6 +84,22 @@ public final class InputMethodInfo implements Parcelable {
public static final String ACTION_STYLUS_HANDWRITING_SETTINGS =
"android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
+ /**
+ * Maximal length of a component name
+ * @hide
+ */
+ @TestApi
+ public static final int COMPONENT_NAME_MAX_LENGTH = 1000;
+
+ /**
+ * The maximum amount of IMEs that are loaded per package (in order).
+ * If a package contains more IMEs, they will be ignored and cannot be enabled.
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("MinMaxConstant")
+ public static final int MAX_IMES_PER_PACKAGE = 20;
+
static final String TAG = "InputMethodInfo";
/**
@@ -252,6 +269,13 @@ public final class InputMethodInfo implements Parcelable {
com.android.internal.R.styleable.InputMethod);
settingsActivityComponent = sa.getString(
com.android.internal.R.styleable.InputMethod_settingsActivity);
+ if ((si.name != null && si.name.length() > COMPONENT_NAME_MAX_LENGTH) || (
+ settingsActivityComponent != null
+ && settingsActivityComponent.length() > COMPONENT_NAME_MAX_LENGTH)) {
+ throw new XmlPullParserException(
+ "Activity name exceeds maximum of 1000 characters");
+ }
+
isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false);
isDefaultResId = sa.getResourceId(
com.android.internal.R.styleable.InputMethod_isDefault, 0);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b89dd31725b7..9f9a7815932c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -52,6 +52,7 @@ import android.graphics.RectF;
import android.graphics.RenderNode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
@@ -3238,6 +3239,44 @@ public class Editor {
.setOnMenuItemClickListener(mOnContextMenuItemClickListener);
mPreserveSelection = true;
+
+ // No-op for the old context menu because it doesn't have icons.
+ adjustIconSpacing(menu);
+ }
+
+ /**
+ * Adjust icon spacing to align the texts.
+ * @hide
+ */
+ @VisibleForTesting
+ public void adjustIconSpacing(ContextMenu menu) {
+ int width = -1;
+ int height = -1;
+ for (int i = 0; i < menu.size(); ++i) {
+ final MenuItem item = menu.getItem(i);
+ final Drawable d = item.getIcon();
+ if (d == null) {
+ continue;
+ }
+
+ width = Math.max(width, d.getIntrinsicWidth());
+ height = Math.max(height, d.getIntrinsicHeight());
+ }
+
+ if (width < 0 || height < 0) {
+ return; // No menu has icon drawable.
+ }
+
+ GradientDrawable paddingDrawable = new GradientDrawable();
+ paddingDrawable.setSize(width, height);
+
+ for (int i = 0; i < menu.size(); ++i) {
+ final MenuItem item = menu.getItem(i);
+ final Drawable d = item.getIcon();
+ if (d == null) {
+ item.setIcon(paddingDrawable);
+ }
+ }
}
@Nullable
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index bf9d3c2eefaf..fd86769293a6 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -34,8 +34,9 @@ oneway interface ITaskOrganizer {
* has create a starting window for the Task.
*
* @param info The information about the Task that's available
+ * @param appToken Token of the application being started.
*/
- void addStartingWindow(in StartingWindowInfo info);
+ void addStartingWindow(in StartingWindowInfo info, IBinder appToken);
/**
* Called when the Task want to remove the starting window.
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 071c20f25e5c..1a58fd556609 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -329,21 +329,6 @@ public class SnapshotDrawerUtils {
}
/**
- * Get or create a TaskDescription from a RunningTaskInfo.
- */
- public static ActivityManager.TaskDescription getOrCreateTaskDescription(
- ActivityManager.RunningTaskInfo runningTaskInfo) {
- final ActivityManager.TaskDescription taskDescription;
- if (runningTaskInfo.taskDescription != null) {
- taskDescription = runningTaskInfo.taskDescription;
- } else {
- taskDescription = new ActivityManager.TaskDescription();
- taskDescription.setBackgroundColor(WHITE);
- }
- return taskDescription;
- }
-
- /**
* Help method to draw the snapshot on a surface.
*/
public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
@@ -359,8 +344,13 @@ public class SnapshotDrawerUtils {
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
- final ActivityManager.TaskDescription taskDescription =
- getOrCreateTaskDescription(runningTaskInfo);
+ final ActivityManager.TaskDescription taskDescription;
+ if (runningTaskInfo.taskDescription != null) {
+ taskDescription = runningTaskInfo.taskDescription;
+ } else {
+ taskDescription = new ActivityManager.TaskDescription();
+ taskDescription.setBackgroundColor(WHITE);
+ }
drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 451acbe84a60..1b64e613ed66 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -22,12 +22,9 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.pm.ActivityInfo;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.view.InsetsState;
-import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
@@ -62,8 +59,6 @@ public final class StartingWindowInfo implements Parcelable {
/** @hide **/
public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4;
- public static final int STARTING_WINDOW_TYPE_WINDOWLESS = 5;
-
/**
* @hide
*/
@@ -72,8 +67,7 @@ public final class StartingWindowInfo implements Parcelable {
STARTING_WINDOW_TYPE_SPLASH_SCREEN,
STARTING_WINDOW_TYPE_SNAPSHOT,
STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN,
- STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN,
- STARTING_WINDOW_TYPE_WINDOWLESS
+ STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
})
public @interface StartingWindowType {}
@@ -124,7 +118,6 @@ public final class StartingWindowInfo implements Parcelable {
TYPE_PARAMETER_ACTIVITY_CREATED,
TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
- TYPE_PARAMETER_WINDOWLESS,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
})
public @interface StartingTypeParams {}
@@ -158,12 +151,6 @@ public final class StartingWindowInfo implements Parcelable {
* @hide
*/
public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080;
-
- /**
- * Windowless surface
- */
- public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100;
-
/**
* Application is allowed to use the legacy splash screen
* @hide
@@ -195,33 +182,7 @@ public final class StartingWindowInfo implements Parcelable {
*/
public TaskSnapshot taskSnapshot;
- @InsetsType public int requestedVisibleTypes = WindowInsets.Type.defaultVisible();
-
- /**
- * App token where the starting window should add to.
- */
- public IBinder appToken;
-
- public IWindowlessStartingSurfaceCallback windowlessStartingSurfaceCallback;
-
- /**
- * The root surface where windowless surface should attach on.
- */
- public SurfaceControl rootSurface;
-
- /**
- * Notify windowless surface is created.
- * @param addedSurface Created surface.
- */
- public void notifyAddComplete(SurfaceControl addedSurface) {
- if (windowlessStartingSurfaceCallback != null) {
- try {
- windowlessStartingSurfaceCallback.onSurfaceAdded(addedSurface);
- } catch (RemoteException e) {
- //
- }
- }
- }
+ public @InsetsType int requestedVisibleTypes = WindowInsets.Type.defaultVisible();
public StartingWindowInfo() {
@@ -255,9 +216,6 @@ public final class StartingWindowInfo implements Parcelable {
dest.writeBoolean(isKeyguardOccluded);
dest.writeTypedObject(taskSnapshot, flags);
dest.writeInt(requestedVisibleTypes);
- dest.writeStrongBinder(appToken);
- dest.writeStrongInterface(windowlessStartingSurfaceCallback);
- dest.writeTypedObject(rootSurface, flags);
}
void readFromParcel(@NonNull Parcel source) {
@@ -272,10 +230,6 @@ public final class StartingWindowInfo implements Parcelable {
isKeyguardOccluded = source.readBoolean();
taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
requestedVisibleTypes = source.readInt();
- appToken = source.readStrongBinder();
- windowlessStartingSurfaceCallback = IWindowlessStartingSurfaceCallback.Stub
- .asInterface(source.readStrongBinder());
- rootSurface = source.readTypedObject(SurfaceControl.CREATOR);
}
@Override
diff --git a/core/java/android/window/StartingWindowRemovalInfo.java b/core/java/android/window/StartingWindowRemovalInfo.java
index 518123600b9a..384dacfe89ed 100644
--- a/core/java/android/window/StartingWindowRemovalInfo.java
+++ b/core/java/android/window/StartingWindowRemovalInfo.java
@@ -67,16 +67,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
*/
public float roundedCornerRadius;
- /**
- * Remove windowless surface.
- */
- public boolean windowlessSurface;
-
- /**
- * Remove immediately.
- */
- public boolean removeImmediately;
-
public StartingWindowRemovalInfo() {
}
@@ -97,8 +87,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
playRevealAnimation = source.readBoolean();
deferRemoveForIme = source.readBoolean();
roundedCornerRadius = source.readFloat();
- windowlessSurface = source.readBoolean();
- removeImmediately = source.readBoolean();
}
@Override
@@ -109,8 +97,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
dest.writeBoolean(playRevealAnimation);
dest.writeBoolean(deferRemoveForIme);
dest.writeFloat(roundedCornerRadius);
- dest.writeBoolean(windowlessSurface);
- dest.writeBoolean(removeImmediately);
}
@Override
@@ -119,9 +105,7 @@ public final class StartingWindowRemovalInfo implements Parcelable {
+ " frame=" + mainFrame
+ " playRevealAnimation=" + playRevealAnimation
+ " roundedCornerRadius=" + roundedCornerRadius
- + " deferRemoveForIme=" + deferRemoveForIme
- + " windowlessSurface=" + windowlessSurface
- + " removeImmediately=" + removeImmediately + "}";
+ + " deferRemoveForIme=" + deferRemoveForIme + "}";
}
public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR =
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index d4728c1187d7..02878f8ae72b 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -92,10 +92,13 @@ public class TaskOrganizer extends WindowOrganizer {
* has create a starting window for the Task.
*
* @param info The information about the Task that's available
+ * @param appToken Token of the application being started.
+ * context to for resources
* @hide
*/
@BinderThread
- public void addStartingWindow(@NonNull StartingWindowInfo info) {}
+ public void addStartingWindow(@NonNull StartingWindowInfo info,
+ @NonNull IBinder appToken) {}
/**
* Called when the Task want to remove the starting window.
@@ -294,8 +297,9 @@ public class TaskOrganizer extends WindowOrganizer {
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
@Override
- public void addStartingWindow(StartingWindowInfo windowInfo) {
- mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo));
+ public void addStartingWindow(StartingWindowInfo windowInfo,
+ IBinder appToken) {
+ mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
}
@Override
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index a8c2b2f28df4..8066f5085a01 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -357,6 +357,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
mImeDispatcher = imeDispatcher;
}
+ /** Returns true if a non-null {@link ImeOnBackInvokedDispatcher} has been set. **/
+ public boolean hasImeOnBackInvokedDispatcher() {
+ return mImeDispatcher != null;
+ }
+
/**
* Class used to check whether a callback can be registered or not. This is meant to be
* shared with {@link ProxyOnBackInvokedDispatcher} which needs to do the same checks.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 7c237e69256f..ace8451722bb 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.content.ContentProvider.getUserIdFromUri;
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
@@ -161,6 +162,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
/**
* The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -1397,7 +1399,7 @@ public class ChooserActivity extends ResolverActivity implements
ImageView previewThumbnailView = contentPreviewLayout.findViewById(
R.id.content_preview_thumbnail);
- if (previewThumbnail == null) {
+ if (!validForContentPreview(previewThumbnail)) {
previewThumbnailView.setVisibility(View.GONE);
} else {
mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
@@ -1427,6 +1429,10 @@ public class ChooserActivity extends ResolverActivity implements
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM, android.net.Uri.class);
+ if (!validForContentPreview(uri)) {
+ imagePreview.setVisibility(View.GONE);
+ return contentPreviewLayout;
+ }
imagePreview.findViewById(R.id.content_preview_image_1_large)
.setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0);
@@ -1436,7 +1442,7 @@ public class ChooserActivity extends ResolverActivity implements
List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, android.net.Uri.class);
List<Uri> imageUris = new ArrayList<>();
for (Uri uri : uris) {
- if (isImageType(resolver.getType(uri))) {
+ if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) {
imageUris.add(uri);
}
}
@@ -1546,9 +1552,16 @@ public class ChooserActivity extends ResolverActivity implements
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM, android.net.Uri.class);
+ if (!validForContentPreview(uri)) {
+ contentPreviewLayout.setVisibility(View.GONE);
+ return contentPreviewLayout;
+ }
loadFileUriIntoView(uri, contentPreviewLayout);
} else {
List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, android.net.Uri.class);
+ uris = uris.stream()
+ .filter(ChooserActivity::validForContentPreview)
+ .collect(Collectors.toList());
int uriCount = uris.size();
if (uriCount == 0) {
@@ -1607,6 +1620,24 @@ public class ChooserActivity extends ResolverActivity implements
}
}
+ /**
+ * Indicate if the incoming content URI should be allowed.
+ *
+ * @param uri the uri to test
+ * @return true if the URI is allowed for content preview
+ */
+ private static boolean validForContentPreview(Uri uri) throws SecurityException {
+ if (uri == null) {
+ return false;
+ }
+ int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT);
+ if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) {
+ Log.e(TAG, "dropped invalid content URI belonging to user " + userId);
+ return false;
+ }
+ return true;
+ }
+
@VisibleForTesting
protected boolean isImageType(String mimeType) {
return mimeType != null && mimeType.startsWith("image/");
diff --git a/core/java/com/android/internal/view/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index b062ca7df921..c7418ee36fff 100644
--- a/core/java/com/android/internal/view/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -14,43 +14,45 @@
* limitations under the License.
*/
-package com.android.internal.view;
+package com.android.internal.inputmethod;
import android.view.inputmethod.ImeTracker;
/**
- * Interface to the global Ime tracker, used by all client applications.
+ * Interface to the global IME tracker service, used by all client applications.
* {@hide}
*/
interface IImeTracker {
/**
- * Called when an IME show request is created,
- * returns a new Binder to be associated with the IME tracking token.
+ * Called when an IME show request is created.
*
+ * @param tag the logging tag.
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created.
+ * @return A new IME tracking token.
*/
- IBinder onRequestShow(int uid, int origin, int reason);
+ ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason);
/**
- * Called when an IME hide request is created,
- * returns a new Binder to be associated with the IME tracking token.
+ * Called when an IME hide request is created.
*
+ * @param tag the logging tag.
* @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created.
+ * @return A new IME tracking token.
*/
- IBinder onRequestHide(int uid, int origin, int reason);
+ ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason);
/**
* Called when the IME request progresses to a further phase.
*
- * @param statsToken the token tracking the current IME request.
+ * @param binder the binder of token tracking the current IME request.
* @param phase the new phase the IME request reached.
*/
- oneway void onProgress(in IBinder statsToken, int phase);
+ oneway void onProgress(in IBinder binder, int phase);
/**
* Called when the IME request fails.
@@ -58,7 +60,7 @@ interface IImeTracker {
* @param statsToken the token tracking the current IME request.
* @param phase the phase the IME request failed at.
*/
- oneway void onFailed(in IBinder statsToken, int phase);
+ oneway void onFailed(in ImeTracker.Token statsToken, int phase);
/**
* Called when the IME request is cancelled.
@@ -66,21 +68,21 @@ interface IImeTracker {
* @param statsToken the token tracking the current IME request.
* @param phase the phase the IME request was cancelled at.
*/
- oneway void onCancelled(in IBinder statsToken, int phase);
+ oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
/**
* Called when the IME show request is successful.
*
* @param statsToken the token tracking the current IME request.
*/
- oneway void onShown(in IBinder statsToken);
+ oneway void onShown(in ImeTracker.Token statsToken);
/**
* Called when the IME hide request is successful.
*
* @param statsToken the token tracking the current IME request.
*/
- oneway void onHidden(in IBinder statsToken);
+ oneway void onHidden(in ImeTracker.Token statsToken);
/**
* Checks whether there are any pending IME visibility requests.
diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java
index 506902f61fd6..426cd74074eb 100644
--- a/core/java/com/android/internal/midi/EventScheduler.java
+++ b/core/java/com/android/internal/midi/EventScheduler.java
@@ -16,7 +16,6 @@
package com.android.internal.midi;
-import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -26,11 +25,11 @@ import java.util.TreeMap;
* And only one Thread can read from the buffer.
*/
public class EventScheduler {
- private static final long NANOS_PER_MILLI = 1000000;
+ public static final long NANOS_PER_MILLI = 1000000;
private final Object mLock = new Object();
- volatile private SortedMap<Long, FastEventQueue> mEventBuffer;
- private FastEventQueue mEventPool = null;
+ protected volatile SortedMap<Long, FastEventQueue> mEventBuffer;
+ protected FastEventQueue mEventPool = null;
private int mMaxPoolSize = 200;
private boolean mClosed;
@@ -38,9 +37,13 @@ public class EventScheduler {
mEventBuffer = new TreeMap<Long, FastEventQueue>();
}
- // If we keep at least one node in the list then it can be atomic
- // and non-blocking.
- private class FastEventQueue {
+ /**
+ * Class for a fast event queue.
+ *
+ * If we keep at least one node in the list then it can be atomic
+ * and non-blocking.
+ */
+ public static class FastEventQueue {
// One thread takes from the beginning of the list.
volatile SchedulableEvent mFirst;
// A second thread returns events to the end of the list.
@@ -48,7 +51,7 @@ public class EventScheduler {
volatile long mEventsAdded;
volatile long mEventsRemoved;
- FastEventQueue(SchedulableEvent event) {
+ public FastEventQueue(SchedulableEvent event) {
mFirst = event;
mLast = mFirst;
mEventsAdded = 1;
@@ -149,7 +152,8 @@ public class EventScheduler {
* @param event
*/
public void add(SchedulableEvent event) {
- synchronized (mLock) {
+ Object lock = getLock();
+ synchronized (lock) {
FastEventQueue list = mEventBuffer.get(event.getTimestamp());
if (list == null) {
long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
@@ -159,7 +163,7 @@ public class EventScheduler {
// If the event we added is earlier than the previous earliest
// event then notify any threads waiting for the next event.
if (event.getTimestamp() < lowestTime) {
- mLock.notify();
+ lock.notify();
}
} else {
list.add(event);
@@ -167,7 +171,7 @@ public class EventScheduler {
}
}
- private SchedulableEvent removeNextEventLocked(long lowestTime) {
+ protected SchedulableEvent removeNextEventLocked(long lowestTime) {
SchedulableEvent event;
FastEventQueue list = mEventBuffer.get(lowestTime);
// Remove list from tree if this is the last node.
@@ -186,7 +190,8 @@ public class EventScheduler {
*/
public SchedulableEvent getNextEvent(long time) {
SchedulableEvent event = null;
- synchronized (mLock) {
+ Object lock = getLock();
+ synchronized (lock) {
if (!mEventBuffer.isEmpty()) {
long lowestTime = mEventBuffer.firstKey();
// Is it time for this list to be processed?
@@ -209,7 +214,8 @@ public class EventScheduler {
*/
public SchedulableEvent waitNextEvent() throws InterruptedException {
SchedulableEvent event = null;
- synchronized (mLock) {
+ Object lock = getLock();
+ synchronized (lock) {
while (!mClosed) {
long millisToWait = Integer.MAX_VALUE;
if (!mEventBuffer.isEmpty()) {
@@ -231,7 +237,7 @@ public class EventScheduler {
}
}
}
- mLock.wait((int) millisToWait);
+ lock.wait((int) millisToWait);
}
}
return event;
@@ -242,10 +248,25 @@ public class EventScheduler {
mEventBuffer = new TreeMap<Long, FastEventQueue>();
}
+ /**
+ * Stops the EventScheduler.
+ * The subscriber calling waitNextEvent() will get one final SchedulableEvent returning null.
+ */
public void close() {
- synchronized (mLock) {
+ Object lock = getLock();
+ synchronized (lock) {
mClosed = true;
- mLock.notify();
+ lock.notify();
}
}
+
+ /**
+ * Gets the lock. This doesn't lock it in anyway.
+ * Subclasses can override this.
+ *
+ * @return Object
+ */
+ protected Object getLock() {
+ return mLock;
+ }
}
diff --git a/core/java/com/android/internal/midi/MidiEventMultiScheduler.java b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java
new file mode 100644
index 000000000000..16e4abecebdd
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+/**
+ * Uses multiple MidiEventSchedulers for waiting for events.
+ *
+ */
+public class MidiEventMultiScheduler {
+ private MultiLockMidiEventScheduler[] mMidiEventSchedulers;
+ private int mNumEventSchedulers;
+ private int mNumClosedSchedulers = 0;
+ private final Object mMultiLock = new Object();
+
+ private class MultiLockMidiEventScheduler extends MidiEventScheduler {
+ @Override
+ public void close() {
+ synchronized (mMultiLock) {
+ mNumClosedSchedulers++;
+ }
+ super.close();
+ }
+
+ @Override
+ protected Object getLock() {
+ return mMultiLock;
+ }
+
+ public boolean isEventBufferEmptyLocked() {
+ return mEventBuffer.isEmpty();
+ }
+
+ public long getLowestTimeLocked() {
+ return mEventBuffer.firstKey();
+ }
+ }
+
+ /**
+ * MidiEventMultiScheduler constructor
+ *
+ * @param numSchedulers the number of schedulers to create
+ */
+ public MidiEventMultiScheduler(int numSchedulers) {
+ mNumEventSchedulers = numSchedulers;
+ mMidiEventSchedulers = new MultiLockMidiEventScheduler[numSchedulers];
+ for (int i = 0; i < numSchedulers; i++) {
+ mMidiEventSchedulers[i] = new MultiLockMidiEventScheduler();
+ }
+ }
+
+ /**
+ * Waits for the next MIDI event. This will return true when it receives it.
+ * If all MidiEventSchedulers have been closed, this will return false.
+ *
+ * @return true if a MIDI event is received and false if all schedulers are closed.
+ */
+ public boolean waitNextEvent() throws InterruptedException {
+ synchronized (mMultiLock) {
+ while (true) {
+ if (mNumClosedSchedulers >= mNumEventSchedulers) {
+ return false;
+ }
+ long lowestTime = Long.MAX_VALUE;
+ long now = System.nanoTime();
+ for (MultiLockMidiEventScheduler eventScheduler : mMidiEventSchedulers) {
+ if (!eventScheduler.isEventBufferEmptyLocked()) {
+ lowestTime = Math.min(lowestTime,
+ eventScheduler.getLowestTimeLocked());
+ }
+ }
+ if (lowestTime <= now) {
+ return true;
+ }
+ long nanosToWait = lowestTime - now;
+ // Add 1 millisecond so we don't wake up before it is
+ // ready.
+ long millisToWait = 1 + (nanosToWait / EventScheduler.NANOS_PER_MILLI);
+ // Clip 64-bit value to 32-bit max.
+ if (millisToWait > Integer.MAX_VALUE) {
+ millisToWait = Integer.MAX_VALUE;
+ }
+ mMultiLock.wait(millisToWait);
+ }
+ }
+ }
+
+ /**
+ * Gets the number of MidiEventSchedulers.
+ *
+ * @return the number of MidiEventSchedulers.
+ */
+ public int getNumEventSchedulers() {
+ return mNumEventSchedulers;
+ }
+
+ /**
+ * Gets a specific MidiEventScheduler based on the index.
+ *
+ * @param index the zero indexed index of a MIDI event scheduler
+ * @return a MidiEventScheduler
+ */
+ public MidiEventScheduler getEventScheduler(int index) {
+ return mMidiEventSchedulers[index];
+ }
+
+ /**
+ * Closes all event schedulers.
+ */
+ public void close() {
+ for (MidiEventScheduler eventScheduler : mMidiEventSchedulers) {
+ eventScheduler.close();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java
index 7b019044110f..1b2934d519fd 100644
--- a/core/java/com/android/internal/midi/MidiEventScheduler.java
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -79,7 +79,7 @@ public class MidiEventScheduler extends EventScheduler {
/**
* Create an event that contains the message.
*/
- private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+ public MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
long timestamp) {
MidiEvent event;
if (count > POOL_EVENT_SIZE) {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 8a9445d8554a..28b98d6fab06 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -16,14 +16,10 @@
package com.android.internal.os;
-import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
-
-import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
import android.app.IActivityManager;
-import android.app.compat.CompatChanges;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.net.TrafficStats;
@@ -40,7 +36,6 @@ import com.android.internal.logging.AndroidConfig;
import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
-import dalvik.system.ZipPathValidator;
import libcore.content.type.MimeMap;
@@ -265,31 +260,10 @@ public class RuntimeInit {
*/
TrafficStats.attachSocketTagger();
- /*
- * Initialize the zip path validator callback depending on the targetSdk.
- */
- initZipPathValidatorCallback();
-
initialized = true;
}
/**
- * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip
- * entry names.
- * Otherwise: clear the callback to the default validation.
- *
- * @hide
- */
- @TestApi
- public static void initZipPathValidatorCallback() {
- if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) {
- ZipPathValidator.setCallback(new SafeZipPathValidatorCallback());
- } else {
- ZipPathValidator.clearCallback();
- }
- }
-
- /**
* Returns an HTTP user agent of the form
* "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)".
*/
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5805d0e050d1..9a4610e8c0a1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -23,11 +23,11 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
+import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputBindResult;
-import com.android.internal.view.IImeTracker;
/**
* Public interface to the global input method manager, used by all client
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 939a0e411913..9c6a534c3bbb 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -266,7 +266,8 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s
}
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
- jlong size, jint channelType, jint fd, jobject hardwareBufferObj) {
+ jint deviceId, jlong size, jint channelType, jint fd,
+ jobject hardwareBufferObj) {
const native_handle_t *nativeHandle = nullptr;
NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0);
@@ -287,7 +288,7 @@ static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa
}
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
- return mgr->createDirectChannel(size, channelType, nativeHandle);
+ return mgr->createDirectChannel(deviceId, size, channelType, nativeHandle);
}
static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
@@ -532,7 +533,7 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled},
- {"nativeCreateDirectChannel", "(JJIILandroid/hardware/HardwareBuffer;)I",
+ {"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I",
(void *)nativeCreateDirectChannel},
{"nativeDestroyDirectChannel", "(JI)V", (void *)nativeDestroyDirectChannel},
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 12eff6723d88..209c78551947 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1304,6 +1304,13 @@
<!-- Default LED off time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOff">2000</integer>
+ <!-- LED behavior when battery is low.
+ Color for solid is taken from config_notificationsBatteryLowARGB
+ 0 - default, solid when charging, flashing when not charging
+ 1 - always solid when battery is low
+ 2 - always flashing when battery is low -->
+ <integer name="config_notificationsBatteryLowBehavior">0</integer>
+
<!-- Default value for led color when battery is low on charge -->
<integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer>
@@ -3065,6 +3072,10 @@
<string name="config_credentialManagerDialogComponent" translatable="false"
>com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
+ <!-- Name of the broadcast receiver that is used to receive provider change events -->
+ <string name="config_credentialManagerReceiverComponent" translatable="false"
+ >com.android.credentialmanager/com.android.credentialmanager.CredentialProviderReceiver</string>
+
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e89fc8b5e46..8d56e7a163a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2004,6 +2004,7 @@
<java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
<java-symbol type="integer" name="config_notificationsBatteryLedOff" />
<java-symbol type="integer" name="config_notificationsBatteryLedOn" />
+ <java-symbol type="integer" name="config_notificationsBatteryLowBehavior" />
<java-symbol type="integer" name="config_notificationsBatteryLowARGB" />
<java-symbol type="integer" name="config_notificationsBatteryMediumARGB" />
<java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" />
@@ -2210,6 +2211,7 @@
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
<java-symbol type="string" name="config_credentialManagerDialogComponent" />
+ <java-symbol type="string" name="config_credentialManagerReceiverComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="string" name="config_persistentDataPackageName" />
<java-symbol type="string" name="config_deviceConfiguratorPackageName" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index e164e08e66e6..4f91e7a3545a 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -16,7 +16,7 @@
package android.app.activity;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
+import static android.content.Context.DEVICE_ID_INVALID;
import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
index f97099d04572..16ed3ef42da3 100644
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
@@ -17,9 +17,15 @@
package android.companion.virtual.sensor;
import static android.hardware.Sensor.TYPE_ACCELEROMETER;
+import static android.hardware.SensorDirectChannel.RATE_STOP;
+import static android.hardware.SensorDirectChannel.RATE_VERY_FAST;
+import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER;
+import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE;
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -40,6 +46,8 @@ public class VirtualSensorConfigTest {
final VirtualSensorConfig originalConfig =
new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
.setVendor(SENSOR_VENDOR)
+ .setHighestDirectReportRateLevel(RATE_VERY_FAST)
+ .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
.build();
final Parcel parcel = Parcel.obtain();
originalConfig.writeToParcel(parcel, /* flags= */ 0);
@@ -49,6 +57,39 @@ public class VirtualSensorConfigTest {
assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType());
assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName());
assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor());
+ assertThat(recreatedConfig.getHighestDirectReportRateLevel()).isEqualTo(RATE_VERY_FAST);
+ assertThat(recreatedConfig.getDirectChannelTypesSupported()).isEqualTo(TYPE_MEMORY_FILE);
+ // From hardware/libhardware/include/hardware/sensors-base.h:
+ // 0x400 is SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM (i.e. TYPE_MEMORY_FILE)
+ // 0x800 is SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC (i.e. TYPE_HARDWARE_BUFFER)
+ // 7 is SENSOR_FLAG_SHIFT_DIRECT_REPORT
+ assertThat(recreatedConfig.getFlags()).isEqualTo(0x400 | RATE_VERY_FAST << 7);
+ }
+
+ @Test
+ public void hardwareBufferDirectChannelTypeSupported_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE));
+ }
+
+ @Test
+ public void directChannelTypeSupported_missingHighestReportRateLevel_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
+ .build());
+ }
+
+ @Test
+ public void directChannelTypeSupported_missingDirectChannelTypeSupported_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
+ .setHighestDirectReportRateLevel(RATE_VERY_FAST)
+ .build());
}
@Test
@@ -56,5 +97,8 @@ public class VirtualSensorConfigTest {
final VirtualSensorConfig config =
new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build();
assertThat(config.getVendor()).isNull();
+ assertThat(config.getHighestDirectReportRateLevel()).isEqualTo(RATE_STOP);
+ assertThat(config.getDirectChannelTypesSupported()).isEqualTo(0);
+ assertThat(config.getFlags()).isEqualTo(0);
}
}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 324f81084f98..d4784374745d 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -16,7 +16,7 @@
package android.content;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.view.Display.DEFAULT_DISPLAY;
diff --git a/core/tests/coretests/src/android/content/pm/UserPackageTest.java b/core/tests/coretests/src/android/content/pm/UserPackageTest.java
new file mode 100644
index 000000000000..5114e2cf9327
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/UserPackageTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.platform.test.annotations.Presubmit;
+
+import junit.framework.TestCase;
+
+@Presubmit
+public class UserPackageTest extends TestCase {
+ public void testCacheLimit() {
+ UserPackage.setValidUserIds(new int[]{0});
+ for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) {
+ UserPackage.of(0, "app" + i);
+ assertEquals(i + 1, UserPackage.numEntriesForUser(0));
+ }
+
+ for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) {
+ UserPackage.of(0, "appOverLimit" + i);
+ final int numCached = UserPackage.numEntriesForUser(0);
+ assertTrue(numCached >= 1);
+ assertTrue(numCached <= UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index ef106bc7cb73..ee1b2aa9a259 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -43,14 +43,56 @@ class FontScaleConverterFactoryTest {
assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
}
- @SmallTest
- fun missingLookupTableReturnsNull() {
- assertThat(FontScaleConverterFactory.forScale(3F)).isNull()
+ @LargeTest
+ @Test
+ fun missingLookupTablePastEnd_returnsLinear() {
+ val table = FontScaleConverterFactory.forScale(3F)!!
+ generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+ .map {
+ assertThat(table.convertSpToDp(it)).isWithin(CONVERSION_TOLERANCE).of(it * 3f)
+ }
+ assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(3f)
+ assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(24f)
+ assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(30f)
+ assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(15f)
+ assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
+ assertThat(table.convertSpToDp(50F)).isWithin(CONVERSION_TOLERANCE).of(150f)
+ assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(300f)
}
@SmallTest
- fun missingLookupTable105ReturnsNull() {
- assertThat(FontScaleConverterFactory.forScale(1.05F)).isNull()
+ fun missingLookupTable110_returnsInterpolated() {
+ val table = FontScaleConverterFactory.forScale(1.1F)!!
+
+ assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.1f)
+ assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.1f)
+ assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(11f)
+ assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.1f)
+ assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
+ assertThat(table.convertSpToDp(50F)).isLessThan(50f * 1.1f)
+ assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.1f)
+ }
+
+ @Test
+ fun missingLookupTable199_returnsInterpolated() {
+ val table = FontScaleConverterFactory.forScale(1.9999F)!!
+ assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f)
+ assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f)
+ assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f)
+ assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f)
+ assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
+ }
+
+ @Test
+ fun missingLookupTable160_returnsInterpolated() {
+ val table = FontScaleConverterFactory.forScale(1.6F)!!
+ assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1f * 1.6F)
+ assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.6F)
+ assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(10f * 1.6F)
+ assertThat(table.convertSpToDp(20F)).isLessThan(20f * 1.6F)
+ assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.6F)
+ assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.6F)
+ assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
}
@SmallTest
@@ -83,7 +125,7 @@ class FontScaleConverterFactoryTest {
@Test
fun allFeasibleScalesAndConversionsDoNotCrash() {
generateSequenceOfFractions(-10f..10f, step = 0.01f)
- .mapNotNull{ FontScaleConverterFactory.forScale(it) }
+ .mapNotNull{ FontScaleConverterFactory.forScale(it) }!!
.flatMap{ table ->
generateSequenceOfFractions(-2000f..2000f, step = 0.01f)
.map{ Pair(table, it) }
diff --git a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
new file mode 100644
index 000000000000..42c97f3876a6
--- /dev/null
+++ b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.os;
+
+import static android.os.CancellationSignalBeamer.Sender;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.CancellationSignalBeamer.Receiver;
+import android.util.PollingCheck;
+import android.util.PollingCheck.PollingCheckCondition;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.util.concurrent.CountDownLatch;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CancellationSignalBeamerTest {
+
+ private CancellationSignal mSenderSignal = new CancellationSignal();
+ private CancellationSignal mReceivedSignal;
+ private Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @Test
+ public void testBeam_null() {
+ try (var token = mSender.beam(null)) {
+ assertThat(token).isNull();
+ invokeGenericService(token);
+ }
+ assertThat(mReceivedSignal).isNull();
+ }
+
+ @Test
+ public void testBeam_nonNull() {
+ try (var token = mSender.beam(mSenderSignal)) {
+ assertThat(token).isNotNull();
+ invokeGenericService(token);
+ }
+ assertThat(mReceivedSignal).isNotNull();
+ }
+
+ @Test
+ public void testBeam_async() {
+ IBinder outerToken;
+ try (var token = mSender.beam(mSenderSignal)) {
+ assertThat(token).isNotNull();
+ outerToken = token;
+ }
+ invokeGenericService(outerToken);
+ assertThat(mReceivedSignal).isNotNull();
+ }
+
+ @Test
+ public void testCancelOnSentSignal_cancelsReceivedSignal() {
+ try (var token = mSender.beam(mSenderSignal)) {
+ invokeGenericService(token);
+ }
+ mSenderSignal.cancel();
+ assertThat(mReceivedSignal.isCanceled()).isTrue();
+ }
+
+ @Test
+ public void testSendingCancelledSignal_cancelsReceivedSignal() {
+ mSenderSignal.cancel();
+ try (var token = mSender.beam(mSenderSignal)) {
+ invokeGenericService(token);
+ }
+ assertThat(mReceivedSignal.isCanceled()).isTrue();
+ }
+
+ @Test
+ public void testUnbeam_null() {
+ assertThat(mReceiver.unbeam(null)).isNull();
+ }
+
+ @Test
+ public void testForget_null() {
+ mReceiver.forget(null);
+ }
+
+ @Test
+ public void testCancel_null() {
+ mReceiver.cancel(null);
+ }
+
+ @Test
+ public void testForget_withUnknownToken() {
+ mReceiver.forget(new Binder());
+ }
+
+ @Test
+ public void testCancel_withUnknownToken() {
+ mReceiver.cancel(new Binder());
+ }
+
+ @Test
+ public void testBinderDied_withUnknownToken() {
+ mReceiver.binderDied(new Binder());
+ }
+
+ @Test
+ public void testReceiverWithCancelOnSenderDead_cancelsOnSenderDeath() {
+ var receiver = new Receiver(true /* cancelOnSenderDeath */);
+ var token = new Binder();
+ var signal = receiver.unbeam(token);
+ receiver.binderDied(token);
+ assertThat(signal.isCanceled()).isTrue();
+ }
+
+ @Test
+ public void testReceiverWithoutCancelOnSenderDead_doesntCancelOnSenderDeath() {
+ var receiver = new Receiver(false /* cancelOnSenderDeath */);
+ var token = new Binder();
+ var signal = receiver.unbeam(token);
+ receiver.binderDied(token);
+ assertThat(signal.isCanceled()).isFalse();
+ }
+
+ @Test
+ public void testDroppingSentSignal_dropsReceivedSignal() throws Exception {
+ // In a multiprocess scenario, sending token over Binder might leak the token
+ // on both ends if we create a reference cycle. Simulate that worst-case scenario
+ // here by leaking it directly, then test that cleanup of the signals still works.
+ var receivedSignalCleaned = new CountDownLatch(1);
+ var tokenRef = new Object[1];
+ // Reference the cancellation signals in a separate method scope, so we don't
+ // accidentally leak them on the stack / in a register.
+ Runnable r = () -> {
+ try (var token = mSender.beam(mSenderSignal)) {
+ tokenRef[0] = token;
+ invokeGenericService(token);
+ }
+ mSenderSignal = null;
+
+ Cleaner.create().register(mReceivedSignal, receivedSignalCleaned::countDown);
+ mReceivedSignal = null;
+ };
+ r.run();
+
+ waitForWithGc(() -> receivedSignalCleaned.getCount() == 0);
+
+ Reference.reachabilityFence(tokenRef[0]);
+ }
+
+ @Test
+ public void testRepeatedBeaming_doesntLeak() throws Exception {
+ var receivedSignalCleaned = new CountDownLatch(1);
+ var tokenRef = new Object[1];
+ // Reference the cancellation signals in a separate method scope, so we don't
+ // accidentally leak them on the stack / in a register.
+ Runnable r = () -> {
+ try (var token = mSender.beam(mSenderSignal)) {
+ tokenRef[0] = token;
+ invokeGenericService(token);
+ }
+ // Beaming again leaves mReceivedSignal dangling, so it should be collected.
+ mSender.beam(mSenderSignal).close();
+
+ Cleaner.create().register(mReceivedSignal, receivedSignalCleaned::countDown);
+ mReceivedSignal = null;
+ };
+ r.run();
+
+ waitForWithGc(() -> receivedSignalCleaned.getCount() == 0);
+
+ Reference.reachabilityFence(tokenRef[0]);
+ }
+
+ private void waitForWithGc(PollingCheckCondition condition) throws IOException {
+ try {
+ PollingCheck.waitFor(() -> {
+ Runtime.getRuntime().gc();
+ return condition.canProceed();
+ });
+ } catch (AssertionError e) {
+ File heap = new File(mContext.getExternalFilesDir(null), "dump.hprof");
+ Debug.dumpHprofData(heap.getAbsolutePath());
+ throw e;
+ }
+ }
+
+ private void invokeGenericService(IBinder cancellationSignalToken) {
+ mReceivedSignal = mReceiver.unbeam(cancellationSignalToken);
+ }
+
+ private final Sender mSender = new Sender() {
+ @Override
+ public void onCancel(IBinder token) {
+ mReceiver.cancel(token);
+ }
+
+ @Override
+ public void onForget(IBinder token) {
+ mReceiver.forget(token);
+ }
+ };
+
+ private final Receiver mReceiver = new Receiver(false);
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ca1367a710ec..06920524acfc 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -248,17 +248,22 @@ public class InsetsControllerTest {
@Test
public void testSystemDrivenInsetsAnimationLoggingListener_onReady() {
+ var loggingListener = mock(WindowInsetsAnimationControlListener.class);
+
prepareControls();
// only the original thread that created view hierarchy can touch its views
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- WindowInsetsAnimationControlListener loggingListener =
- mock(WindowInsetsAnimationControlListener.class);
mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
- verify(loggingListener).onReady(notNull(), anyInt());
+ // When using the animation thread, this must not invoke onReady()
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
});
+ // Wait for onReady() being dispatched on the animation thread.
+ InsetsAnimationThread.get().getThreadHandler().runWithScissors(() -> {}, 500);
+
+ verify(loggingListener).onReady(notNull(), anyInt());
}
@Test
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index 1db6587e1283..6fa8f1117343 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.SIZE;
+import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -51,11 +52,13 @@ public class InsetsSourceTest {
private final InsetsSource mSource = new InsetsSource(0 /* id */, navigationBars());
private final InsetsSource mImeSource = new InsetsSource(1 /* id */, ime());
+ private final InsetsSource mCaptionSource = new InsetsSource(2 /* id */, captionBar());
@Before
public void setUp() {
mSource.setVisible(true);
mImeSource.setVisible(true);
+ mCaptionSource.setVisible(true);
}
@Test
@@ -107,6 +110,17 @@ public class InsetsSourceTest {
}
@Test
+ public void testCalculateInsets_caption_resizing() {
+ mCaptionSource.setFrame(new Rect(0, 0, 100, 100));
+ Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ insets = mCaptionSource.calculateInsets(new Rect(0, 0, 50, 200), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ insets = mCaptionSource.calculateInsets(new Rect(100, 100, 200, 500), false);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
public void testCalculateInsets_invisible() {
mSource.setFrame(new Rect(0, 0, 500, 100));
mSource.setVisible(false);
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index b035c23cb98b..fde1a6d7b04c 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -248,6 +248,18 @@ public class InsetsStateTest {
}
@Test
+ public void testCalculateInsets_captionBarOffset() {
+ mState.getOrCreateSource(ID_CAPTION_BAR, captionBar())
+ .setFrame(new Rect(0, 0, 100, 300))
+ .setVisible(true);
+
+ Insets visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
+ assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
+ }
+
+ @Test
public void testCalculateInsets_extraNavRightStatusTop() {
mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
.setFrame(new Rect(0, 0, 100, 100))
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index 0c7550e79cfa..777246b83a57 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -33,6 +33,8 @@ import android.app.Activity;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.view.ContextMenu;
import android.view.MenuItem;
@@ -167,4 +169,75 @@ public class TextViewContextMenuTest {
assertThat(idCaptor.getValue()).isEqualTo(TextView.ID_ASSIST);
assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE);
}
+
+ @UiThreadTest
+ @Test
+ public void testAdjustIconSpaces() {
+ GradientDrawable gd = new GradientDrawable();
+ gd.setSize(128, 256);
+
+ // Setup mocks
+ ContextMenu menu = mock(ContextMenu.class);
+
+ MenuItem mockIconMenu = newMockMenuItem();
+ when(mockIconMenu.getIcon()).thenReturn(gd);
+
+ MenuItem mockNoIconMenu = newMockMenuItem();
+ when(mockNoIconMenu.getIcon()).thenReturn(null);
+
+ MenuItem mockNoIconMenu2 = newMockMenuItem();
+ when(mockNoIconMenu2.getIcon()).thenReturn(null);
+
+ when(menu.size()).thenReturn(3);
+ when(menu.getItem(0)).thenReturn(mockIconMenu);
+ when(menu.getItem(1)).thenReturn(mockNoIconMenu);
+ when(menu.getItem(2)).thenReturn(mockNoIconMenu2);
+
+
+ // Execute the test method
+ EditText et = mActivity.findViewById(R.id.editText);
+ Editor editor = et.getEditorForTesting();
+ editor.adjustIconSpacing(menu);
+
+ // Verify
+ ArgumentCaptor<Drawable> drawableCaptor = ArgumentCaptor.forClass(Drawable.class);
+ verify(mockNoIconMenu).setIcon(drawableCaptor.capture());
+
+ Drawable paddingDrawable = drawableCaptor.getValue();
+ assertThat(paddingDrawable).isNotNull();
+ assertThat(paddingDrawable.getIntrinsicWidth()).isEqualTo(128);
+ assertThat(paddingDrawable.getIntrinsicHeight()).isEqualTo(256);
+
+ ArgumentCaptor<Drawable> drawableCaptor2 = ArgumentCaptor.forClass(Drawable.class);
+ verify(mockNoIconMenu2).setIcon(drawableCaptor2.capture());
+
+ Drawable paddingDrawable2 = drawableCaptor2.getValue();
+ assertThat(paddingDrawable2).isSameInstanceAs(paddingDrawable);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testAdjustIconSpacesNoIconCase() {
+ // Setup mocks
+ ContextMenu menu = mock(ContextMenu.class);
+
+ MenuItem mockNoIconMenu = newMockMenuItem();
+ when(mockNoIconMenu.getIcon()).thenReturn(null);
+
+ MenuItem mockNoIconMenu2 = newMockMenuItem();
+ when(mockNoIconMenu2.getIcon()).thenReturn(null);
+
+ when(menu.size()).thenReturn(2);
+ when(menu.getItem(0)).thenReturn(mockNoIconMenu);
+ when(menu.getItem(1)).thenReturn(mockNoIconMenu2);
+
+ // Execute the test method
+ EditText et = mActivity.findViewById(R.id.editText);
+ Editor editor = et.getEditorForTesting();
+ editor.adjustIconSpacing(menu);
+
+ // Verify
+ verify(mockNoIconMenu, times(0)).setIcon(any());
+ verify(mockNoIconMenu2, times(0)).setIcon(any());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java b/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java
deleted file mode 100644
index c540a150bf35..000000000000
--- a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import static org.junit.Assert.assertThrows;
-
-import android.compat.testing.PlatformCompatChangeRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-/**
- * Test SafeZipPathCallback.
- */
-@RunWith(AndroidJUnit4.class)
-public class SafeZipPathValidatorCallbackTest {
- @Rule
- public TestRule mCompatChangeRule = new PlatformCompatChangeRule();
-
- @Before
- public void setUp() {
- RuntimeInit.initZipPathValidatorCallback();
- }
-
- @Test
- @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void testNewZipFile_whenZipFileHasDangerousEntriesAndChangeEnabled_throws()
- throws Exception {
- final String[] dangerousEntryNames = {
- "../foo.bar",
- "foo/../bar.baz",
- "foo/../../bar.baz",
- "foo.bar/..",
- "foo.bar/../",
- "..",
- "../",
- "/foo",
- };
- for (String entryName : dangerousEntryNames) {
- final File tempFile = File.createTempFile("smdc", "zip");
- try {
- writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName);
-
- assertThrows(
- "ZipException expected for entry: " + entryName,
- ZipException.class,
- () -> {
- new ZipFile(tempFile);
- });
- } finally {
- tempFile.delete();
- }
- }
- }
-
- @Test
- @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void
- testZipInputStreamGetNextEntry_whenZipFileHasDangerousEntriesAndChangeEnabled_throws()
- throws Exception {
- final String[] dangerousEntryNames = {
- "../foo.bar",
- "foo/../bar.baz",
- "foo/../../bar.baz",
- "foo.bar/..",
- "foo.bar/../",
- "..",
- "../",
- "/foo",
- };
- for (String entryName : dangerousEntryNames) {
- byte[] badZipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName);
- try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(badZipBytes))) {
- assertThrows(
- "ZipException expected for entry: " + entryName,
- ZipException.class,
- () -> {
- zis.getNextEntry();
- });
- }
- }
- }
-
- @Test
- @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void testNewZipFile_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow()
- throws Exception {
- final String[] normalEntryNames = {
- "foo", "foo.bar", "foo..bar",
- };
- for (String entryName : normalEntryNames) {
- final File tempFile = File.createTempFile("smdc", "zip");
- try {
- writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName);
- try {
- new ZipFile((tempFile));
- } catch (ZipException e) {
- throw new AssertionError("ZipException not expected for entry: " + entryName);
- }
- } finally {
- tempFile.delete();
- }
- }
- }
-
- @Test
- @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void
- testZipInputStreamGetNextEntry_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow()
- throws Exception {
- final String[] normalEntryNames = {
- "foo", "foo.bar", "foo..bar",
- };
- for (String entryName : normalEntryNames) {
- byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName);
- try {
- ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes));
- zis.getNextEntry();
- } catch (ZipException e) {
- throw new AssertionError("ZipException not expected for entry: " + entryName);
- }
- }
- }
-
- @Test
- @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void
- testNewZipFile_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow()
- throws Exception {
- final String[] entryNames = {
- "../foo.bar",
- "foo/../bar.baz",
- "foo/../../bar.baz",
- "foo.bar/..",
- "foo.bar/../",
- "..",
- "../",
- "/foo",
- "foo",
- "foo.bar",
- "foo..bar",
- };
- for (String entryName : entryNames) {
- final File tempFile = File.createTempFile("smdc", "zip");
- try {
- writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName);
- try {
- new ZipFile((tempFile));
- } catch (ZipException e) {
- throw new AssertionError("ZipException not expected for entry: " + entryName);
- }
- } finally {
- tempFile.delete();
- }
- }
- }
-
- @Test
- @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL})
- public void
- testZipInputStreamGetNextEntry_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow()
- throws Exception {
- final String[] entryNames = {
- "../foo.bar",
- "foo/../bar.baz",
- "foo/../../bar.baz",
- "foo.bar/..",
- "foo.bar/../",
- "..",
- "../",
- "/foo",
- "foo",
- "foo.bar",
- "foo..bar",
- };
- for (String entryName : entryNames) {
- byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName);
- try {
- ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes));
- zis.getNextEntry();
- } catch (ZipException e) {
- throw new AssertionError("ZipException not expected for entry: " + entryName);
- }
- }
- }
-
- private void writeZipFileOutputStreamWithEmptyEntry(File tempFile, String entryName)
- throws IOException {
- FileOutputStream tempFileStream = new FileOutputStream(tempFile);
- writeZipOutputStreamWithEmptyEntry(tempFileStream, entryName);
- tempFileStream.close();
- }
-
- private byte[] getZipBytesFromZipOutputStreamWithEmptyEntry(String entryName)
- throws IOException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- writeZipOutputStreamWithEmptyEntry(bos, entryName);
- return bos.toByteArray();
- }
-
- private void writeZipOutputStreamWithEmptyEntry(OutputStream os, String entryName)
- throws IOException {
- ZipOutputStream zos = new ZipOutputStream(os);
- ZipEntry entry = new ZipEntry(entryName);
- zos.putNextEntry(entry);
- zos.write(new byte[2]);
- zos.closeEntry();
- zos.close();
- }
-}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
index 9ccf3b3c9d05..3b8b8c797241 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTest.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -117,6 +117,31 @@ public class DisplayTest {
}
@Test
+ public void testGetReportedHdrTypes_returns_mode_specific_hdr_types() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ float[] alternativeRefreshRates = new float[0];
+ int[] hdrTypesWithDv = new int[] {1, 2, 3, 4};
+ Display.Mode modeWithDv = new Display.Mode(/* modeId= */ 0, 0, 0, 0f,
+ alternativeRefreshRates, hdrTypesWithDv);
+
+ int[] hdrTypesWithoutDv = new int[]{2, 3, 4};
+ Display.Mode modeWithoutDv = new Display.Mode(/* modeId= */ 1, 0, 0, 0f,
+ alternativeRefreshRates, hdrTypesWithoutDv);
+
+ mDisplayInfo.supportedModes = new Display.Mode[] {modeWithoutDv, modeWithDv};
+ mDisplayInfo.hdrCapabilities = new Display.HdrCapabilities(hdrTypesWithDv, 0, 0, 0);
+
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+
+ mDisplayInfo.modeId = 0;
+ assertArrayEquals(hdrTypesWithDv, display.getReportedHdrTypes());
+
+ mDisplayInfo.modeId = 1;
+ assertArrayEquals(hdrTypesWithoutDv, display.getReportedHdrTypes());
+ }
+
+ @Test
public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
setDisplayInfoPortrait(mDisplayInfo);
final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f3318f400e21..5cb5ffa03e00 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -471,7 +471,7 @@ applications that come with the platform
<permission name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED"/>
<!-- Permission required for CTS test - CallAudioInterceptionTest -->
<permission name="android.permission.CALL_AUDIO_INTERCEPTION"/>
- <!-- Permission required for CTS test - CtsPermission5TestCases -->
+ <!-- Permission required for CTS test - CtsAttributionSourceTestCases -->
<permission name="android.permission.RENOUNCE_PERMISSIONS" />
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index 6a1313e16ce5..66fabec91924 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -341,7 +341,6 @@ public class Mesh {
* @hide so only calls from module can utilize it
*/
long getNativeWrapperInstance() {
- nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed);
return mNativeMeshWrapper;
}
@@ -383,5 +382,4 @@ public class Mesh {
private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values);
- private static native void nativeUpdateMesh(long nativeMeshWrapper, boolean mIsIndexed);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b6fd0bbafc71..585f81c81a36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -428,9 +428,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
@Override
- public void addStartingWindow(StartingWindowInfo info) {
+ public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingWindow != null) {
- mStartingWindow.addStartingWindow(info);
+ mStartingWindow.addStartingWindow(info, appToken);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
index 04bff97bc4ce..306d6196c553 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -32,6 +32,7 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
@@ -115,7 +116,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
boolean latest) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
- if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
+ if (TransitionUtil.isClosingType(mPending.get(i).mType) == closing) {
return mPending.get(i);
}
if (latest) {
@@ -148,7 +149,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
final TaskViewTaskController taskView = findTaskView(triggerTask);
if (taskView == null) return null;
// Opening types should all be initiated by shell
- if (!Transitions.isClosingType(request.getType())) return null;
+ if (!TransitionUtil.isClosingType(request.getType())) return null;
PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
if (pending == null) {
pending = new PendingTransition(request.getType(), null, taskView, null /* cookie */);
@@ -238,7 +239,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change chg = info.getChanges().get(i);
if (chg.getTaskInfo() == null) continue;
- if (Transitions.isClosingType(chg.getMode())) {
+ if (TransitionUtil.isClosingType(chg.getMode())) {
final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
TaskViewTaskController tv = findTaskView(chg.getTaskInfo());
if (tv == null) {
@@ -255,7 +256,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
tv.prepareCloseAnimation();
}
changesHandled++;
- } else if (Transitions.isOpeningType(chg.getMode())) {
+ } else if (TransitionUtil.isOpeningType(chg.getMode())) {
final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
final TaskViewTaskController tv;
if (taskIsNew) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index 579f7aae9321..c767376d4f29 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -29,7 +29,7 @@ import android.window.TransitionInfo;
import androidx.annotation.NonNull;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
/**
* Wrapper to handle the ActivityEmbedding animation update in one
@@ -90,7 +90,7 @@ class ActivityEmbeddingAnimationAdapter {
mChange = change;
mLeash = leash;
mWholeAnimationBounds.set(wholeAnimationBounds);
- if (Transitions.isClosingType(change.getMode())) {
+ if (TransitionUtil.isClosingType(change.getMode())) {
// When it is closing, we want to show the content at the start position in case the
// window is resizing as well. For example, when the activities is changing from split
// to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index fe3c4ea3fee9..1df6ecda78c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -43,7 +43,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.common.ScreenshotUtils;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
import java.util.List;
@@ -184,7 +184,7 @@ class ActivityEmbeddingAnimationRunner {
if (isChangeTransition) {
return createChangeAnimationAdapters(info, startTransaction);
}
- if (Transitions.isClosingType(info.getType())) {
+ if (TransitionUtil.isClosingType(info.getType())) {
return createCloseAnimationAdapters(info);
}
return createOpenAnimationAdapters(info);
@@ -219,7 +219,7 @@ class ActivityEmbeddingAnimationRunner {
final Rect openingWholeScreenBounds = new Rect();
final Rect closingWholeScreenBounds = new Rect();
for (TransitionInfo.Change change : info.getChanges()) {
- if (Transitions.isOpeningType(change.getMode())) {
+ if (TransitionUtil.isOpeningType(change.getMode())) {
openingChanges.add(change);
openingWholeScreenBounds.union(change.getEndAbsBounds());
} else {
@@ -271,7 +271,7 @@ class ActivityEmbeddingAnimationRunner {
continue;
}
final TransitionInfo.Change change = adapter.mChange;
- if (Transitions.isOpeningType(adapter.mChange.getMode())) {
+ if (TransitionUtil.isOpeningType(adapter.mChange.getMode())) {
// Need to screenshot after startTransaction is applied otherwise activity
// may not be visible or ready yet.
postStartTransactionCallbacks.add(
@@ -343,7 +343,7 @@ class ActivityEmbeddingAnimationRunner {
// When the parent window is also included in the transition as an opening window,
// we would like to animate the parent window instead.
final TransitionInfo.Change parentChange = info.getChange(parentToken);
- if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
+ if (parentChange != null && TransitionUtil.isOpeningType(parentChange.getMode())) {
// We won't create a separate animation for the parent, but to animate the
// parent for the child resizing.
handledChanges.add(parentChange);
@@ -404,7 +404,7 @@ class ActivityEmbeddingAnimationRunner {
// No-op if it will be covered by the changing parent window, or it is a changing
// window without bounds change.
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
- } else if (Transitions.isClosingType(change.getMode())) {
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
shouldShouldBackgroundColor = false;
} else {
@@ -469,7 +469,7 @@ class ActivityEmbeddingAnimationRunner {
// When the parent window is also included in the transition as an opening window,
// we would like to animate the parent window instead.
final TransitionInfo.Change parentChange = info.getChange(parentToken);
- if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
+ if (parentChange != null && TransitionUtil.isOpeningType(parentChange.getMode())) {
changingChanges.add(parentChange);
}
}
@@ -491,8 +491,8 @@ class ActivityEmbeddingAnimationRunner {
// No-op if it will be covered by the changing parent window.
continue;
}
- hasOpeningWindow |= Transitions.isOpeningType(change.getMode());
- hasClosingWindow |= Transitions.isClosingType(change.getMode());
+ hasOpeningWindow |= TransitionUtil.isOpeningType(change.getMode());
+ hasClosingWindow |= TransitionUtil.isClosingType(change.getMode());
}
return hasOpeningWindow && hasClosingWindow;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index d10a6744b5f1..cb8342a10a6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -36,7 +36,7 @@ import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import com.android.internal.policy.TransitionAnimation;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
/** Animation spec for ActivityEmbedding transition. */
// TODO(b/206557124): provide an easier way to customize animation
@@ -73,7 +73,7 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
static Animation createNoopAnimation(@NonNull TransitionInfo.Change change) {
// Noop but just keep the window showing/hiding.
- final float alpha = Transitions.isClosingType(change.getMode()) ? 0f : 1f;
+ final float alpha = TransitionUtil.isClosingType(change.getMode()) ? 0f : 1f;
return new AlphaAnimation(alpha, alpha);
}
@@ -198,7 +198,7 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
Animation loadOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
- final boolean isEnter = Transitions.isOpeningType(change.getMode());
+ final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
final Animation animation;
if (shouldShowBackdrop(info, change)) {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
@@ -222,7 +222,7 @@ class ActivityEmbeddingAnimationSpec {
@NonNull
Animation loadCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
- final boolean isEnter = Transitions.isOpeningType(change.getMode());
+ final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
final Animation animation;
if (shouldShowBackdrop(info, change)) {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
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 e5c0570841f4..6b0337d3fb4a 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
@@ -40,7 +40,6 @@ import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.animation.Animator;
import android.app.ActivityManager;
@@ -72,6 +71,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.Optional;
@@ -702,7 +702,7 @@ public class PipTransition extends PipTransitionController {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change == enterPip) continue;
- if (isOpeningType(change.getMode())) {
+ if (TransitionUtil.isOpeningType(change.getMode())) {
final SurfaceControl leash = change.getLeash();
startTransaction.show(leash).setAlpha(leash, 1.f);
}
@@ -873,7 +873,7 @@ public class PipTransition extends PipTransitionController {
continue;
}
- if (isOpeningType(mode) && change.getParent() == null) {
+ if (TransitionUtil.isOpeningType(mode) && change.getParent() == null) {
final SurfaceControl leash = change.getLeash();
final Rect endBounds = change.getEndAbsBounds();
startTransaction
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 520da92fad72..c96323a340b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -53,6 +53,7 @@ import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
@@ -531,7 +532,7 @@ class SplitScreenTransitions {
}
private boolean isOpeningTransition(TransitionInfo info) {
- return Transitions.isOpeningType(info.getType())
+ return TransitionUtil.isOpeningType(info.getType())
|| info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
|| info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
}
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 7833cfe7df9d..8b24d86a568f 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
@@ -61,8 +61,8 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonT
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
-import static com.android.wm.shell.transition.Transitions.isClosingType;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
+import static com.android.wm.shell.util.TransitionUtil.isClosingType;
+import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -135,6 +135,7 @@ import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
+import com.android.wm.shell.util.TransitionUtil;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -2271,7 +2272,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Use normal animations.
return false;
- } else if (mMixedHandler != null && Transitions.hasDisplayChange(info)) {
+ } else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) {
// A display-change has been un-expectedly inserted into the transition. Redirect
// handling to the mixed-handler to deal with splitting it up.
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java
deleted file mode 100644
index 1ddd8f9a3a14..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.startingsurface;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.hardware.display.DisplayManager;
-import android.view.Display;
-
-import com.android.wm.shell.common.ShellExecutor;
-
-// abstract class to create splash screen window(or windowless window)
-abstract class AbsSplashWindowCreator {
- protected static final String TAG = StartingWindowController.TAG;
- protected final SplashscreenContentDrawer mSplashscreenContentDrawer;
- protected final Context mContext;
- protected final DisplayManager mDisplayManager;
- protected final ShellExecutor mSplashScreenExecutor;
- protected final StartingSurfaceDrawer.StartingWindowRecordManager mStartingWindowRecordManager;
-
- private StartingSurface.SysuiProxy mSysuiProxy;
-
- AbsSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context,
- ShellExecutor splashScreenExecutor, DisplayManager displayManager,
- StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
- mSplashscreenContentDrawer = contentDrawer;
- mContext = context;
- mSplashScreenExecutor = splashScreenExecutor;
- mDisplayManager = displayManager;
- mStartingWindowRecordManager = startingWindowRecordManager;
- }
-
- int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
- return splashScreenThemeResId != 0
- ? splashScreenThemeResId
- : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
- : com.android.internal.R.style.Theme_DeviceDefault_DayNight;
- }
-
- protected Display getDisplay(int displayId) {
- return mDisplayManager.getDisplay(displayId);
- }
-
- void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
- mSysuiProxy = sysuiProxy;
- }
-
- protected void requestTopUi(boolean requestTopUi) {
- if (mSysuiProxy != null) {
- mSysuiProxy.requestTopUi(requestTopUi, TAG);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java
deleted file mode 100644
index 20c4d5ae5f58..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.startingsurface;
-
-import android.window.StartingWindowInfo;
-import android.window.TaskSnapshot;
-
-import com.android.wm.shell.common.ShellExecutor;
-
-class SnapshotWindowCreator {
- private final ShellExecutor mMainExecutor;
- private final StartingSurfaceDrawer.StartingWindowRecordManager
- mStartingWindowRecordManager;
-
- SnapshotWindowCreator(ShellExecutor mainExecutor,
- StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
- mMainExecutor = mainExecutor;
- mStartingWindowRecordManager = startingWindowRecordManager;
- }
-
- void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) {
- final int taskId = startingWindowInfo.taskInfo.taskId;
- // Remove any existing starting window for this task before adding.
- mStartingWindowRecordManager.removeWindow(taskId, true);
- final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo,
- startingWindowInfo.appToken, snapshot, mMainExecutor,
- () -> mStartingWindowRecordManager.removeWindow(taskId, true));
- if (surface != null) {
- final SnapshotWindowRecord tView = new SnapshotWindowRecord(surface,
- startingWindowInfo.taskInfo.topActivityType, mMainExecutor);
- mStartingWindowRecordManager.addRecord(taskId, tView);
- }
- }
-
- private static class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
- private final TaskSnapshotWindow mTaskSnapshotWindow;
-
- SnapshotWindowRecord(TaskSnapshotWindow taskSnapshotWindow,
- int activityType, ShellExecutor removeExecutor) {
- super(activityType, removeExecutor);
- mTaskSnapshotWindow = taskSnapshotWindow;
- mBGColor = mTaskSnapshotWindow.getBackgroundColor();
- }
-
- @Override
- protected void removeImmediately() {
- super.removeImmediately();
- mTaskSnapshotWindow.removeImmediately();
- }
-
- @Override
- protected boolean hasImeSurface() {
- return mTaskSnapshotWindow.hasImeSurface();
- }
- }
-}
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 79cd891741d6..ebb957b2201b 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
@@ -24,6 +24,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
+
import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -93,25 +96,6 @@ import java.util.function.UnaryOperator;
public class SplashscreenContentDrawer {
private static final String TAG = StartingWindowController.TAG;
- /**
- * The minimum duration during which the splash screen is shown when the splash screen icon is
- * animated.
- */
- static final long MINIMAL_ANIMATION_DURATION = 400L;
-
- /**
- * Allow the icon style splash screen to be displayed for longer to give time for the animation
- * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly
- * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration.
- */
- static final long TIME_WINDOW_DURATION = 100L;
-
- /**
- * The maximum duration during which the splash screen will be shown if the application is ready
- * to show before the icon animation finishes.
- */
- static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
-
// The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
// icon which it's non-transparent foreground area is similar to it's background area, then
// do not enlarge the foreground drawable.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
deleted file mode 100644
index 4db81e232f20..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.startingsurface;
-
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
-import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
-import android.app.TaskInfo;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.os.IBinder;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.WindowInsetsController;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.widget.FrameLayout;
-import android.window.SplashScreenView;
-import android.window.StartingWindowInfo;
-import android.window.StartingWindowRemovalInfo;
-
-import com.android.internal.R;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-
-import java.util.function.Supplier;
-
-/**
- * A class which able to draw splash screen as the starting window for a task.
- *
- * In order to speed up, there will use two threads to creating a splash screen in parallel.
- * Right now we are still using PhoneWindow to create splash screen window, so the view is added to
- * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call
- * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view
- * can synchronize on each frame.
- *
- * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing
- * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background
- * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after
- * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very
- * quickly.
- *
- * So basically we are using the spare time to prepare the SplashScreenView while splash screen
- * thread is waiting for
- * 1. WindowManager#addView(binder call to WM),
- * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device),
- * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will
- * always happen before #draw).
- * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on
- * splash-screen background tread can make they execute in parallel, which ensure it is faster then
- * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame.
- *
- * Here is the sequence to compare the difference between using single and two thread.
- *
- * Single thread:
- * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout
- * -> draw -> AdaptiveIconDrawable#draw
- *
- * Two threads:
- * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw)
- * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint
- * directly).
- */
-class SplashscreenWindowCreator extends AbsSplashWindowCreator {
- private static final int LIGHT_BARS_MASK =
- WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
- | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-
- private final WindowManagerGlobal mWindowManagerGlobal;
- private Choreographer mChoreographer;
-
- /**
- * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
- * rendered and that have not yet been removed by their client.
- */
- private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
- new SparseArray<>(1);
-
- SplashscreenWindowCreator(SplashscreenContentDrawer contentDrawer, Context context,
- ShellExecutor splashScreenExecutor, DisplayManager displayManager,
- StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
- super(contentDrawer, context, splashScreenExecutor, displayManager,
- startingWindowRecordManager);
- mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
- mWindowManagerGlobal = WindowManagerGlobal.getInstance();
- }
-
- void addSplashScreenStartingWindow(StartingWindowInfo windowInfo,
- @StartingWindowInfo.StartingWindowType int suggestType) {
- final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
- final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
- ? windowInfo.targetActivityInfo
- : taskInfo.topActivityInfo;
- if (activityInfo == null || activityInfo.packageName == null) {
- return;
- }
- // replace with the default theme if the application didn't set
- final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
- final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
- suggestType, mDisplayManager);
- if (context == null) {
- return;
- }
- final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
- context, windowInfo, suggestType, activityInfo.packageName,
- suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
- ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken);
-
- final int displayId = taskInfo.displayId;
- final int taskId = taskInfo.taskId;
- final Display display = getDisplay(displayId);
-
- // TODO(b/173975965) tracking performance
- // Prepare the splash screen content view on splash screen worker thread in parallel, so the
- // content view won't be blocked by binder call like addWindow and relayout.
- // 1. Trigger splash screen worker thread to create SplashScreenView before/while
- // Session#addWindow.
- // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start
- // traversal, which will call Session#relayout on splash screen thread.
- // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at
- // the same time the splash screen thread should be executing Session#relayout. Blocking the
- // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready.
-
- // Record whether create splash screen view success, notify to current thread after
- // create splash screen view finished.
- final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
- final FrameLayout rootLayout = new FrameLayout(
- mSplashscreenContentDrawer.createViewContextWrapper(context));
- rootLayout.setPadding(0, 0, 0, 0);
- rootLayout.setFitsSystemWindows(false);
- final Runnable setViewSynchronized = () -> {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView");
- // waiting for setContentView before relayoutWindow
- SplashScreenView contentView = viewSupplier.get();
- final StartingSurfaceDrawer.StartingWindowRecord sRecord =
- mStartingWindowRecordManager.getRecord(taskId);
- final SplashWindowRecord record = sRecord instanceof SplashWindowRecord
- ? (SplashWindowRecord) sRecord : null;
- // If record == null, either the starting window added fail or removed already.
- // Do not add this view if the token is mismatch.
- if (record != null && windowInfo.appToken == record.mAppToken) {
- // if view == null then creation of content view was failed.
- if (contentView != null) {
- try {
- rootLayout.addView(contentView);
- } catch (RuntimeException e) {
- Slog.w(TAG, "failed set content view to starting window "
- + "at taskId: " + taskId, e);
- contentView = null;
- }
- }
- record.setSplashScreenView(contentView);
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- };
- requestTopUi(true);
- mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo,
- viewSupplier::setView, viewSupplier::setUiThreadInitTask);
- try {
- if (addWindow(taskId, windowInfo.appToken, rootLayout, display, params, suggestType)) {
- // We use the splash screen worker thread to create SplashScreenView while adding
- // the window, as otherwise Choreographer#doFrame might be delayed on this thread.
- // And since Choreographer#doFrame won't happen immediately after adding the window,
- // if the view is not added to the PhoneWindow on the first #doFrame, the view will
- // not be rendered on the first frame. So here we need to synchronize the view on
- // the window before first round relayoutWindow, which will happen after insets
- // animation.
- mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
- final SplashWindowRecord record =
- (SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId);
- if (record != null) {
- record.parseAppSystemBarColor(context);
- // Block until we get the background color.
- final SplashScreenView contentView = viewSupplier.get();
- if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
- contentView.addOnAttachStateChangeListener(
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- final int lightBarAppearance =
- ContrastColorUtil.isColorLight(
- contentView.getInitBackgroundColor())
- ? LIGHT_BARS_MASK : 0;
- contentView.getWindowInsetsController()
- .setSystemBarsAppearance(
- lightBarAppearance, LIGHT_BARS_MASK);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- }
- });
- }
- }
- } 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
- // failure loading resources because we are loading from an app
- // on external storage that has been unmounted.
- Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
- }
- }
-
- int estimateTaskBackgroundColor(TaskInfo taskInfo) {
- if (taskInfo.topActivityInfo == null) {
- return Color.TRANSPARENT;
- }
- final ActivityInfo activityInfo = taskInfo.topActivityInfo;
- final String packageName = activityInfo.packageName;
- final int userId = taskInfo.userId;
- final Context windowContext;
- try {
- windowContext = mContext.createPackageContextAsUser(
- packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId));
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Failed creating package context with package name "
- + packageName + " for user " + taskInfo.userId, e);
- return Color.TRANSPARENT;
- }
- try {
- final IPackageManager packageManager = ActivityThread.getPackageManager();
- final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName,
- userId);
- final int splashScreenThemeId = splashScreenThemeName != null
- ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null)
- : 0;
-
- final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo);
-
- if (theme != windowContext.getThemeResId()) {
- windowContext.setTheme(theme);
- }
- return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext);
- } catch (RuntimeException | RemoteException e) {
- Slog.w(TAG, "failed get starting window background color at taskId: "
- + taskInfo.taskId, e);
- }
- return Color.TRANSPARENT;
- }
-
- /**
- * Called when the Task wants to copy the splash screen.
- */
- public void copySplashScreenView(int taskId) {
- final StartingSurfaceDrawer.StartingWindowRecord record =
- mStartingWindowRecordManager.getRecord(taskId);
- final SplashWindowRecord preView = record instanceof SplashWindowRecord
- ? (SplashWindowRecord) record : null;
- SplashScreenView.SplashScreenViewParcelable parcelable;
- SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null;
- if (splashScreenView != null && splashScreenView.isCopyable()) {
- parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView);
- parcelable.setClientCallback(
- new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
- () -> onAppSplashScreenViewRemoved(taskId, false))));
- splashScreenView.onCopied();
- mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
- } else {
- parcelable = null;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "Copying splash screen window view for task: %d with parcelable %b",
- taskId, parcelable != null);
- ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
- }
-
- /**
- * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy
- * or when the Activity is clean up.
- *
- * @param taskId The Task id on which the splash screen was attached
- */
- public void onAppSplashScreenViewRemoved(int taskId) {
- onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
- }
-
- /**
- * @param fromServer If true, this means the removal was notified by the server. This is only
- * used for debugging purposes.
- * @see #onAppSplashScreenViewRemoved(int)
- */
- private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
- SurfaceControlViewHost viewHost =
- mAnimatedSplashScreenSurfaceHosts.get(taskId);
- if (viewHost == null) {
- return;
- }
- mAnimatedSplashScreenSurfaceHosts.remove(taskId);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "%s the splash screen. Releasing SurfaceControlViewHost for task: %d",
- fromServer ? "Server cleaned up" : "App removed", taskId);
- SplashScreenView.releaseIconHost(viewHost);
- }
-
- protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
- WindowManager.LayoutParams params,
- @StartingWindowInfo.StartingWindowType int suggestType) {
- boolean shouldSaveView = true;
- final Context context = view.getContext();
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
- mWindowManagerGlobal.addView(view, params, display,
- null /* parentWindow */, context.getUserId());
- } catch (WindowManager.BadTokenException e) {
- // ignore
- Slog.w(TAG, appToken + " already running, starting window not displayed. "
- + e.getMessage());
- shouldSaveView = false;
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- if (view.getParent() == null) {
- Slog.w(TAG, "view not successfully added to wm, removing view");
- mWindowManagerGlobal.removeView(view, true /* immediate */);
- shouldSaveView = false;
- }
- }
- if (shouldSaveView) {
- mStartingWindowRecordManager.removeWindow(taskId, true);
- saveSplashScreenRecord(appToken, taskId, view, suggestType);
- }
- return shouldSaveView;
- }
-
- private void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
- @StartingWindowInfo.StartingWindowType int suggestType) {
- final SplashWindowRecord tView =
- new SplashWindowRecord(appToken, view, suggestType);
- mStartingWindowRecordManager.addRecord(taskId, tView);
- }
-
- private void removeWindowInner(View decorView, boolean hideView) {
- requestTopUi(false);
- if (hideView) {
- decorView.setVisibility(View.GONE);
- }
- mWindowManagerGlobal.removeView(decorView, false /* immediate */);
- }
-
- private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
- private SplashScreenView mView;
- private boolean mIsViewSet;
- private Runnable mUiThreadInitTask;
- void setView(SplashScreenView view) {
- synchronized (this) {
- mView = view;
- mIsViewSet = true;
- notify();
- }
- }
-
- void setUiThreadInitTask(Runnable initTask) {
- synchronized (this) {
- mUiThreadInitTask = initTask;
- }
- }
-
- @Override
- @Nullable
- public SplashScreenView get() {
- synchronized (this) {
- while (!mIsViewSet) {
- try {
- wait();
- } catch (InterruptedException ignored) {
- }
- }
- if (mUiThreadInitTask != null) {
- mUiThreadInitTask.run();
- mUiThreadInitTask = null;
- }
- return mView;
- }
- }
- }
-
- private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord {
- private final IBinder mAppToken;
- private final View mRootView;
- @StartingWindowInfo.StartingWindowType private final int mSuggestType;
- private final long mCreateTime;
-
- private boolean mSetSplashScreen;
- private SplashScreenView mSplashView;
- private int mSystemBarAppearance;
- private boolean mDrawsSystemBarBackgrounds;
-
- SplashWindowRecord(IBinder appToken, View decorView,
- @StartingWindowInfo.StartingWindowType int suggestType) {
- mAppToken = appToken;
- mRootView = decorView;
- mSuggestType = suggestType;
- mCreateTime = SystemClock.uptimeMillis();
- }
-
- void setSplashScreenView(SplashScreenView splashScreenView) {
- if (mSetSplashScreen) {
- return;
- }
- mSplashView = splashScreenView;
- mBGColor = mSplashView.getInitBackgroundColor();
- mSetSplashScreen = true;
- }
-
- void parseAppSystemBarColor(Context context) {
- final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
- mDrawsSystemBarBackgrounds = a.getBoolean(
- R.styleable.Window_windowDrawsSystemBarBackgrounds, false);
- if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
- mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
- }
- if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
- mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
- }
- a.recycle();
- }
-
- // Reset the system bar color which set by splash screen, make it align to the app.
- void clearSystemBarColor() {
- if (mRootView == null || !mRootView.isAttachedToWindow()) {
- return;
- }
- if (mRootView.getLayoutParams() instanceof WindowManager.LayoutParams) {
- final WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mRootView.getLayoutParams();
- if (mDrawsSystemBarBackgrounds) {
- lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- } else {
- lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- }
- mRootView.setLayoutParams(lp);
- }
- mRootView.getWindowInsetsController().setSystemBarsAppearance(
- mSystemBarAppearance, LIGHT_BARS_MASK);
- }
-
- @Override
- public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
- if (mRootView != null) {
- if (mSplashView != null) {
- clearSystemBarColor();
- if (immediately
- || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
- removeWindowInner(mRootView, false);
- } else {
- if (info.playRevealAnimation) {
- mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
- info.windowAnimationLeash, info.mainFrame,
- () -> removeWindowInner(mRootView, true),
- mCreateTime, info.roundedCornerRadius);
- } else {
- // the SplashScreenView has been copied to client, hide the view to skip
- // default exit animation
- removeWindowInner(mRootView, true);
- }
- }
- } else {
- // shouldn't happen
- Slog.e(TAG, "Found empty splash screen, remove!");
- removeWindowInner(mRootView, false);
- }
- }
- }
- }
-}
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 ff06db370d1a..4f07bfeacce5 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
@@ -16,80 +16,169 @@
package com.android.wm.shell.startingsurface;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
-import android.annotation.CallSuper;
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
import android.app.TaskInfo;
-import android.app.WindowConfiguration;
import android.content.Context;
-import android.content.res.Configuration;
+import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.Slog;
import android.util.SparseArray;
-import android.view.IWindow;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
+import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
import android.window.SplashScreenView;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskSnapshot;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ContrastColorUtil;
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.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import java.util.function.Supplier;
+
/**
* A class which able to draw splash screen or snapshot as the starting window for a task.
+ *
+ * In order to speed up, there will use two threads to creating a splash screen in parallel.
+ * Right now we are still using PhoneWindow to create splash screen window, so the view is added to
+ * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call
+ * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view
+ * can synchronize on each frame.
+ *
+ * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing
+ * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background
+ * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after
+ * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very
+ * quickly.
+ *
+ * So basically we are using the spare time to prepare the SplashScreenView while splash screen
+ * thread is waiting for
+ * 1. WindowManager#addView(binder call to WM),
+ * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device),
+ * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will
+ * always happen before #draw).
+ * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on
+ * splash-screen background tread can make they execute in parallel, which ensure it is faster then
+ * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame.
+ *
+ * Here is the sequence to compare the difference between using single and two thread.
+ *
+ * Single thread:
+ * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout
+ * -> draw -> AdaptiveIconDrawable#draw
+ *
+ * Two threads:
+ * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw)
+ * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint
+ * directly).
*/
@ShellSplashscreenThread
public class StartingSurfaceDrawer {
+ private static final String TAG = StartingWindowController.TAG;
+ private final Context mContext;
+ private final DisplayManager mDisplayManager;
private final ShellExecutor mSplashScreenExecutor;
@VisibleForTesting
final SplashscreenContentDrawer mSplashscreenContentDrawer;
- @VisibleForTesting
- final SplashscreenWindowCreator mSplashscreenWindowCreator;
- private final SnapshotWindowCreator mSnapshotWindowCreator;
- private final WindowlessSplashWindowCreator mWindowlessSplashWindowCreator;
- private final WindowlessSnapshotWindowCreator mWindowlessSnapshotWindowCreator;
+ private Choreographer mChoreographer;
+ private final WindowManagerGlobal mWindowManagerGlobal;
+ private StartingSurface.SysuiProxy mSysuiProxy;
+ private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
+
+ private static final int LIGHT_BARS_MASK =
+ WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
+ | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+ /**
+ * The minimum duration during which the splash screen is shown when the splash screen icon is
+ * animated.
+ */
+ static final long MINIMAL_ANIMATION_DURATION = 400L;
+
+ /**
+ * Allow the icon style splash screen to be displayed for longer to give time for the animation
+ * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly
+ * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration.
+ */
+ static final long TIME_WINDOW_DURATION = 100L;
+
+ /**
+ * The maximum duration during which the splash screen will be shown if the application is ready
+ * to show before the icon animation finishes.
+ */
+ static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
- @VisibleForTesting
- final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager();
- // Windowless surface could co-exist with starting window in a task.
- @VisibleForTesting
- final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager();
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
IconProvider iconProvider, TransactionPool pool) {
+ mContext = context;
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
mSplashScreenExecutor = splashScreenExecutor;
- final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(context, iconProvider, pool);
- displayManager.getDisplay(DEFAULT_DISPLAY);
-
- mSplashscreenWindowCreator = new SplashscreenWindowCreator(mSplashscreenContentDrawer,
- context, splashScreenExecutor, displayManager, mWindowRecords);
- mSnapshotWindowCreator = new SnapshotWindowCreator(splashScreenExecutor,
- mWindowRecords);
- mWindowlessSplashWindowCreator = new WindowlessSplashWindowCreator(
- mSplashscreenContentDrawer, context, splashScreenExecutor, displayManager,
- mWindowlessRecords, pool);
- mWindowlessSnapshotWindowCreator = new WindowlessSnapshotWindowCreator(
- mWindowlessRecords, context, displayManager, mSplashscreenContentDrawer, pool);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
+ mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
+ mWindowManagerGlobal = WindowManagerGlobal.getInstance();
+ mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ }
+
+ @VisibleForTesting
+ final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+
+ /**
+ * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
+ * rendered and that have not yet been removed by their client.
+ */
+ private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
+ new SparseArray<>(1);
+
+ private Display getDisplay(int displayId) {
+ return mDisplayManager.getDisplay(displayId);
+ }
+
+ int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
+ return splashScreenThemeResId != 0
+ ? splashScreenThemeResId
+ : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
+ : com.android.internal.R.style.Theme_DeviceDefault_DayNight;
}
void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
- mSplashscreenWindowCreator.setSysuiProxy(sysuiProxy);
- mWindowlessSplashWindowCreator.setSysuiProxy(sysuiProxy);
+ mSysuiProxy = sysuiProxy;
}
/**
@@ -97,55 +186,231 @@ public class StartingSurfaceDrawer {
*
* @param suggestType The suggestion type to draw the splash screen.
*/
- void addSplashScreenStartingWindow(StartingWindowInfo windowInfo,
+ void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken,
@StartingWindowType int suggestType) {
- mSplashscreenWindowCreator.addSplashScreenStartingWindow(windowInfo, suggestType);
+ final RunningTaskInfo taskInfo = windowInfo.taskInfo;
+ final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
+ ? windowInfo.targetActivityInfo
+ : taskInfo.topActivityInfo;
+ if (activityInfo == null || activityInfo.packageName == null) {
+ return;
+ }
+ // replace with the default theme if the application didn't set
+ final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
+ final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
+ suggestType, mDisplayManager);
+ if (context == null) {
+ return;
+ }
+ final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
+ context, windowInfo, suggestType, activityInfo.packageName,
+ suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken);
+
+ final int displayId = taskInfo.displayId;
+ final int taskId = taskInfo.taskId;
+ final Display display = getDisplay(displayId);
+
+ // TODO(b/173975965) tracking performance
+ // Prepare the splash screen content view on splash screen worker thread in parallel, so the
+ // content view won't be blocked by binder call like addWindow and relayout.
+ // 1. Trigger splash screen worker thread to create SplashScreenView before/while
+ // Session#addWindow.
+ // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start
+ // traversal, which will call Session#relayout on splash screen thread.
+ // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at
+ // the same time the splash screen thread should be executing Session#relayout. Blocking the
+ // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready.
+
+ // Record whether create splash screen view success, notify to current thread after
+ // create splash screen view finished.
+ final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
+ final FrameLayout rootLayout = new FrameLayout(
+ mSplashscreenContentDrawer.createViewContextWrapper(context));
+ rootLayout.setPadding(0, 0, 0, 0);
+ rootLayout.setFitsSystemWindows(false);
+ final Runnable setViewSynchronized = () -> {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView");
+ // waiting for setContentView before relayoutWindow
+ SplashScreenView contentView = viewSupplier.get();
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ // If record == null, either the starting window added fail or removed already.
+ // Do not add this view if the token is mismatch.
+ if (record != null && appToken == record.mAppToken) {
+ // if view == null then creation of content view was failed.
+ if (contentView != null) {
+ try {
+ rootLayout.addView(contentView);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "failed set content view to starting window "
+ + "at taskId: " + taskId, e);
+ contentView = null;
+ }
+ }
+ record.setSplashScreenView(contentView);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ };
+ if (mSysuiProxy != null) {
+ mSysuiProxy.requestTopUi(true, TAG);
+ }
+ mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo,
+ viewSupplier::setView, viewSupplier::setUiThreadInitTask);
+ try {
+ if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
+ // We use the splash screen worker thread to create SplashScreenView while adding
+ // the window, as otherwise Choreographer#doFrame might be delayed on this thread.
+ // And since Choreographer#doFrame won't happen immediately after adding the window,
+ // if the view is not added to the PhoneWindow on the first #doFrame, the view will
+ // not be rendered on the first frame. So here we need to synchronize the view on
+ // the window before first round relayoutWindow, which will happen after insets
+ // animation.
+ mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ record.parseAppSystemBarColor(context);
+ // Block until we get the background color.
+ final SplashScreenView contentView = viewSupplier.get();
+ if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+ contentView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ final int lightBarAppearance = ContrastColorUtil.isColorLight(
+ contentView.getInitBackgroundColor())
+ ? LIGHT_BARS_MASK : 0;
+ contentView.getWindowInsetsController().setSystemBarsAppearance(
+ lightBarAppearance, LIGHT_BARS_MASK);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
+ }
+ 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
+ // failure loading resources because we are loading from an app
+ // on external storage that has been unmounted.
+ Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
+ }
}
int getStartingWindowBackgroundColorForTask(int taskId) {
- final StartingWindowRecord startingWindowRecord = mWindowRecords.getRecord(taskId);
+ final StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId);
if (startingWindowRecord == null) {
return Color.TRANSPARENT;
}
- return startingWindowRecord.getBGColor();
+ return startingWindowRecord.mBGColor;
+ }
+
+ private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
+ private SplashScreenView mView;
+ private boolean mIsViewSet;
+ private Runnable mUiThreadInitTask;
+ void setView(SplashScreenView view) {
+ synchronized (this) {
+ mView = view;
+ mIsViewSet = true;
+ notify();
+ }
+ }
+
+ void setUiThreadInitTask(Runnable initTask) {
+ synchronized (this) {
+ mUiThreadInitTask = initTask;
+ }
+ }
+
+ @Override
+ @Nullable
+ public SplashScreenView get() {
+ synchronized (this) {
+ while (!mIsViewSet) {
+ try {
+ wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ if (mUiThreadInitTask != null) {
+ mUiThreadInitTask.run();
+ mUiThreadInitTask = null;
+ }
+ return mView;
+ }
+ }
}
int estimateTaskBackgroundColor(TaskInfo taskInfo) {
- return mSplashscreenWindowCreator.estimateTaskBackgroundColor(taskInfo);
+ if (taskInfo.topActivityInfo == null) {
+ return Color.TRANSPARENT;
+ }
+ final ActivityInfo activityInfo = taskInfo.topActivityInfo;
+ final String packageName = activityInfo.packageName;
+ final int userId = taskInfo.userId;
+ final Context windowContext;
+ try {
+ windowContext = mContext.createPackageContextAsUser(
+ packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId));
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Failed creating package context with package name "
+ + packageName + " for user " + taskInfo.userId, e);
+ return Color.TRANSPARENT;
+ }
+ try {
+ final IPackageManager packageManager = ActivityThread.getPackageManager();
+ final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName,
+ userId);
+ final int splashScreenThemeId = splashScreenThemeName != null
+ ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null)
+ : 0;
+
+ final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo);
+
+ if (theme != windowContext.getThemeResId()) {
+ windowContext.setTheme(theme);
+ }
+ return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext);
+ } catch (RuntimeException | RemoteException e) {
+ Slog.w(TAG, "failed get starting window background color at taskId: "
+ + taskInfo.taskId, e);
+ }
+ return Color.TRANSPARENT;
}
/**
* Called when a task need a snapshot starting window.
*/
- void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) {
- mSnapshotWindowCreator.makeTaskSnapshotWindow(startingWindowInfo, snapshot);
+ void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken,
+ TaskSnapshot snapshot) {
+ final int taskId = startingWindowInfo.taskInfo.taskId;
+ // Remove any existing starting window for this task before adding.
+ removeWindowNoAnimate(taskId);
+ final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
+ snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId));
+ if (surface == null) {
+ return;
+ }
+ final StartingWindowRecord tView = new StartingWindowRecord(appToken,
+ null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT);
+ mStartingWindowRecords.put(taskId, tView);
}
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
- if (removalInfo.windowlessSurface) {
- mWindowlessRecords.removeWindow(removalInfo, removalInfo.removeImmediately);
- } else {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "Task start finish, remove starting surface for task: %d",
- removalInfo.taskId);
- mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately);
- }
- }
-
- /**
- * Create a windowless starting surface and attach to the root surface.
- */
- void addWindowlessStartingSurface(StartingWindowInfo windowInfo) {
- if (windowInfo.taskSnapshot != null) {
- mWindowlessSnapshotWindowCreator.makeTaskSnapshotWindow(windowInfo,
- windowInfo.rootSurface, windowInfo.taskSnapshot, mSplashScreenExecutor);
- } else {
- mWindowlessSplashWindowCreator.addSplashScreenStartingWindow(
- windowInfo, windowInfo.rootSurface);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Task start finish, remove starting surface for task: %d",
+ removalInfo.taskId);
+ removeWindowSynced(removalInfo, false /* immediately */);
}
/**
@@ -154,15 +419,37 @@ public class StartingSurfaceDrawer {
public void clearAllWindows() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Clear all starting windows immediately");
- mWindowRecords.clearAllWindows();
- mWindowlessRecords.clearAllWindows();
+ final int taskSize = mStartingWindowRecords.size();
+ final int[] taskIds = new int[taskSize];
+ for (int i = taskSize - 1; i >= 0; --i) {
+ taskIds[i] = mStartingWindowRecords.keyAt(i);
+ }
+ for (int i = taskSize - 1; i >= 0; --i) {
+ removeWindowNoAnimate(taskIds[i]);
+ }
}
/**
* Called when the Task wants to copy the splash screen.
*/
public void copySplashScreenView(int taskId) {
- mSplashscreenWindowCreator.copySplashScreenView(taskId);
+ final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
+ SplashScreenViewParcelable parcelable;
+ SplashScreenView splashScreenView = preView != null ? preView.mContentView : null;
+ if (splashScreenView != null && splashScreenView.isCopyable()) {
+ parcelable = new SplashScreenViewParcelable(splashScreenView);
+ parcelable.setClientCallback(
+ new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
+ () -> onAppSplashScreenViewRemoved(taskId, false))));
+ splashScreenView.onCopied();
+ mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
+ } else {
+ parcelable = null;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Copying splash screen window view for task: %d with parcelable %b",
+ taskId, parcelable != null);
+ ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
/**
@@ -172,148 +459,195 @@ public class StartingSurfaceDrawer {
* @param taskId The Task id on which the splash screen was attached
*/
public void onAppSplashScreenViewRemoved(int taskId) {
- mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(taskId);
+ onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
}
- void onImeDrawnOnTask(int taskId) {
- onImeDrawnOnTask(mWindowRecords, taskId);
- onImeDrawnOnTask(mWindowlessRecords, taskId);
- }
-
- private void onImeDrawnOnTask(StartingWindowRecordManager records, int taskId) {
- final StartingSurfaceDrawer.StartingWindowRecord sRecord =
- records.getRecord(taskId);
- final SnapshotRecord record = sRecord instanceof SnapshotRecord
- ? (SnapshotRecord) sRecord : null;
- if (record != null && record.hasImeSurface()) {
- records.removeWindow(taskId, true);
+ /**
+ * @param fromServer If true, this means the removal was notified by the server. This is only
+ * used for debugging purposes.
+ * @see #onAppSplashScreenViewRemoved(int)
+ */
+ private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
+ SurfaceControlViewHost viewHost =
+ mAnimatedSplashScreenSurfaceHosts.get(taskId);
+ if (viewHost == null) {
+ return;
}
+ mAnimatedSplashScreenSurfaceHosts.remove(taskId);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "%s the splash screen. Releasing SurfaceControlViewHost for task: %d",
+ fromServer ? "Server cleaned up" : "App removed", taskId);
+ SplashScreenView.releaseIconHost(viewHost);
}
- static class WindowlessStartingWindow extends WindowlessWindowManager {
- SurfaceControl mChildSurface;
-
- WindowlessStartingWindow(Configuration c, SurfaceControl rootSurface) {
- super(c, rootSurface, null /* hostInputToken */);
- }
-
- @Override
- protected SurfaceControl getParentSurface(IWindow window,
- WindowManager.LayoutParams attrs) {
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("Windowless window")
- .setHidden(false)
- .setParent(mRootSurface)
- .setCallsite("WindowlessStartingWindow#attachToParentSurface");
- mChildSurface = builder.build();
- try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
- t.setLayer(mChildSurface, Integer.MAX_VALUE);
- t.apply();
+ protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
+ WindowManager.LayoutParams params, @StartingWindowType int suggestType) {
+ boolean shouldSaveView = true;
+ final Context context = view.getContext();
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
+ mWindowManagerGlobal.addView(view, params, display,
+ null /* parentWindow */, context.getUserId());
+ } catch (WindowManager.BadTokenException e) {
+ // ignore
+ Slog.w(TAG, appToken + " already running, starting window not displayed. "
+ + e.getMessage());
+ shouldSaveView = false;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (view.getParent() == null) {
+ Slog.w(TAG, "view not successfully added to wm, removing view");
+ mWindowManagerGlobal.removeView(view, true /* immediate */);
+ shouldSaveView = false;
}
- return mChildSurface;
}
- }
- abstract static class StartingWindowRecord {
- protected int mBGColor;
- abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately);
- int getBGColor() {
- return mBGColor;
+ if (shouldSaveView) {
+ removeWindowNoAnimate(taskId);
+ saveSplashScreenRecord(appToken, taskId, view, suggestType);
}
+ return shouldSaveView;
}
- abstract static class SnapshotRecord extends StartingWindowRecord {
- private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
- /**
- * The max delay time in milliseconds for removing the task snapshot window with IME
- * visible.
- * Ideally the delay time will be shorter when receiving
- * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
- */
- private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
- private final Runnable mScheduledRunnable = this::removeImmediately;
-
- @WindowConfiguration.ActivityType protected final int mActivityType;
- protected final ShellExecutor mRemoveExecutor;
-
- SnapshotRecord(int activityType, ShellExecutor removeExecutor) {
- mActivityType = activityType;
- mRemoveExecutor = removeExecutor;
- }
-
- @Override
- public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
- if (immediately) {
- removeImmediately();
- } else {
- scheduleRemove(info.deferRemoveForIme);
- }
- }
-
- void scheduleRemove(boolean deferRemoveForIme) {
- // Show the latest content as soon as possible for unlocking to home.
- if (mActivityType == ACTIVITY_TYPE_HOME) {
- removeImmediately();
- return;
- }
- mRemoveExecutor.removeCallbacks(mScheduledRunnable);
- final long delayRemovalTime = hasImeSurface() && deferRemoveForIme
- ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
- : DELAY_REMOVAL_TIME_GENERAL;
- mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "Defer removing snapshot surface in %d", delayRemovalTime);
- }
+ @VisibleForTesting
+ void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
+ @StartingWindowType int suggestType) {
+ final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
+ null/* TaskSnapshotWindow */, suggestType);
+ mStartingWindowRecords.put(taskId, tView);
+ }
- protected abstract boolean hasImeSurface();
+ private void removeWindowNoAnimate(int taskId) {
+ mTmpRemovalInfo.taskId = taskId;
+ removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
+ }
- @CallSuper
- protected void removeImmediately() {
- mRemoveExecutor.removeCallbacks(mScheduledRunnable);
+ void onImeDrawnOnTask(int taskId) {
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null && record.mTaskSnapshotWindow != null
+ && record.mTaskSnapshotWindow.hasImeSurface()) {
+ removeWindowNoAnimate(taskId);
}
}
- static class StartingWindowRecordManager {
- private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
- private final SparseArray<StartingWindowRecord> mStartingWindowRecords =
- new SparseArray<>();
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) {
+ final int taskId = removalInfo.taskId;
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null) {
+ if (record.mDecorView != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Removing splash screen window for task: %d", taskId);
+ if (record.mContentView != null) {
+ record.clearSystemBarColor();
+ if (immediately
+ || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+ removeWindowInner(record.mDecorView, false);
+ } else {
+ if (removalInfo.playRevealAnimation) {
+ mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+ removalInfo.windowAnimationLeash, removalInfo.mainFrame,
+ () -> removeWindowInner(record.mDecorView, true),
+ record.mCreateTime, removalInfo.roundedCornerRadius);
+ } else {
+ // the SplashScreenView has been copied to client, hide the view to skip
+ // default exit animation
+ removeWindowInner(record.mDecorView, true);
+ }
+ }
+ } else {
+ // shouldn't happen
+ Slog.e(TAG, "Found empty splash screen, remove!");
+ removeWindowInner(record.mDecorView, false);
+ }
- void clearAllWindows() {
- final int taskSize = mStartingWindowRecords.size();
- final int[] taskIds = new int[taskSize];
- for (int i = taskSize - 1; i >= 0; --i) {
- taskIds[i] = mStartingWindowRecords.keyAt(i);
}
- for (int i = taskSize - 1; i >= 0; --i) {
- removeWindow(taskIds[i], true);
+ if (record.mTaskSnapshotWindow != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Removing task snapshot window for %d", taskId);
+ if (immediately) {
+ record.mTaskSnapshotWindow.removeImmediately();
+ } else {
+ record.mTaskSnapshotWindow.scheduleRemove(removalInfo.deferRemoveForIme);
+ }
}
+ mStartingWindowRecords.remove(taskId);
}
+ }
- void addRecord(int taskId, StartingWindowRecord record) {
- mStartingWindowRecords.put(taskId, record);
+ private void removeWindowInner(View decorView, boolean hideView) {
+ if (mSysuiProxy != null) {
+ mSysuiProxy.requestTopUi(false, TAG);
+ }
+ if (hideView) {
+ decorView.setVisibility(View.GONE);
}
+ mWindowManagerGlobal.removeView(decorView, false /* immediate */);
+ }
- void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) {
- final int taskId = removeInfo.taskId;
- final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
- if (record != null) {
- record.removeIfPossible(removeInfo, immediately);
- mStartingWindowRecords.remove(taskId);
+ /**
+ * Record the view or surface for a starting window.
+ */
+ private static class StartingWindowRecord {
+ private final IBinder mAppToken;
+ private final View mDecorView;
+ private final TaskSnapshotWindow mTaskSnapshotWindow;
+ private SplashScreenView mContentView;
+ private boolean mSetSplashScreen;
+ @StartingWindowType private int mSuggestType;
+ private int mBGColor;
+ private final long mCreateTime;
+ private int mSystemBarAppearance;
+ private boolean mDrawsSystemBarBackgrounds;
+
+ StartingWindowRecord(IBinder appToken, View decorView,
+ TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) {
+ mAppToken = appToken;
+ mDecorView = decorView;
+ mTaskSnapshotWindow = taskSnapshotWindow;
+ if (mTaskSnapshotWindow != null) {
+ mBGColor = mTaskSnapshotWindow.getBackgroundColor();
}
+ mSuggestType = suggestType;
+ mCreateTime = SystemClock.uptimeMillis();
}
- void removeWindow(int taskId, boolean immediately) {
- mTmpRemovalInfo.taskId = taskId;
- removeWindow(mTmpRemovalInfo, immediately);
+ private void setSplashScreenView(SplashScreenView splashScreenView) {
+ if (mSetSplashScreen) {
+ return;
+ }
+ mContentView = splashScreenView;
+ mSetSplashScreen = true;
}
- StartingWindowRecord getRecord(int taskId) {
- return mStartingWindowRecords.get(taskId);
+ private void parseAppSystemBarColor(Context context) {
+ final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
+ mDrawsSystemBarBackgrounds = a.getBoolean(
+ R.styleable.Window_windowDrawsSystemBarBackgrounds, false);
+ if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
+ mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+ }
+ if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
+ mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+ }
+ a.recycle();
}
- @VisibleForTesting
- int recordSize() {
- return mStartingWindowRecords.size();
+ // Reset the system bar color which set by splash screen, make it align to the app.
+ private void clearSystemBarColor() {
+ if (mDecorView == null || !mDecorView.isAttachedToWindow()) {
+ return;
+ }
+ if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) {
+ final WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+ if (mDrawsSystemBarBackgrounds) {
+ lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ } else {
+ lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ }
+ mDecorView.setLayoutParams(lp);
+ }
+ mDecorView.getWindowInsetsController().setSystemBarsAppearance(
+ mSystemBarAppearance, LIGHT_BARS_MASK);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index bec4ba3bf0d1..be2e79342d07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -21,7 +21,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
@@ -30,6 +29,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Color;
+import android.os.IBinder;
import android.os.Trace;
import android.util.SparseIntArray;
import android.window.StartingWindowInfo;
@@ -152,23 +152,22 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
/**
* Called when a task need a starting window.
*/
- public void addStartingWindow(StartingWindowInfo windowInfo) {
+ public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() -> {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType(
windowInfo);
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
- if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) {
- mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo);
- } else if (isSplashScreenType(suggestionType)) {
- mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType);
+ if (isSplashScreenType(suggestionType)) {
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
+ suggestionType);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
final TaskSnapshot snapshot = windowInfo.taskSnapshot;
- mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
+ snapshot);
}
- if (suggestionType != STARTING_WINDOW_TYPE_NONE
- && suggestionType != STARTING_WINDOW_TYPE_WINDOWLESS) {
+ if (suggestionType != STARTING_WINDOW_TYPE_NONE) {
int taskId = runningTaskInfo.taskId;
int color = mStartingSurfaceDrawer
.getStartingWindowBackgroundColorForTask(taskId);
@@ -219,13 +218,11 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow(
removalInfo));
- if (!removalInfo.windowlessSurface) {
- mSplashScreenExecutor.executeDelayed(() -> {
- synchronized (mTaskBackgroundColors) {
- mTaskBackgroundColors.delete(removalInfo.taskId);
- }
- }, TASK_BG_COLOR_RETAIN_TIME_MS);
- }
+ mSplashScreenExecutor.executeDelayed(() -> {
+ synchronized (mTaskBackgroundColors) {
+ mTaskBackgroundColors.delete(removalInfo.taskId);
+ }
+ }, TASK_BG_COLOR_RETAIN_TIME_MS);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index c964df1452e0..a05ed4f24a08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -62,14 +63,24 @@ public class TaskSnapshotWindow {
private static final String TAG = StartingWindowController.TAG;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=";
+ private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
+ /**
+ * The max delay time in milliseconds for removing the task snapshot window with IME visible.
+ * Ideally the delay time will be shorter when receiving
+ * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
+ */
+ private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
+
private final Window mWindow;
private final Runnable mClearWindowHandler;
private final ShellExecutor mSplashScreenExecutor;
private final IWindowSession mSession;
private boolean mHasDrawn;
private final Paint mBackgroundPaint = new Paint();
+ private final int mActivityType;
private final int mOrientationOnCreation;
+ private final Runnable mScheduledRunnable = this::removeImmediately;
private final boolean mHasImeSurface;
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
@@ -93,6 +104,7 @@ public class TaskSnapshotWindow {
final Point taskSize = snapshot.getTaskSize();
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
final int orientation = snapshot.getOrientation();
+ final int activityType = runningTaskInfo.topActivityType;
final int displayId = runningTaskInfo.displayId;
final IWindowSession session = WindowManagerGlobal.getWindowSession();
@@ -102,11 +114,16 @@ public class TaskSnapshotWindow {
final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
- final TaskDescription taskDescription =
- SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
+ final TaskDescription taskDescription;
+ if (runningTaskInfo.taskDescription != null) {
+ taskDescription = runningTaskInfo.taskDescription;
+ } else {
+ taskDescription = new TaskDescription();
+ taskDescription.setBackgroundColor(WHITE);
+ }
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
- snapshot, taskDescription, orientation,
+ snapshot, taskDescription, orientation, activityType,
clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
@@ -136,8 +153,6 @@ public class TaskSnapshotWindow {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
- Slog.w(TAG, "Failed to relayout snapshot starting window");
- return null;
}
SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
@@ -149,7 +164,7 @@ public class TaskSnapshotWindow {
}
public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription,
- int currentOrientation, Runnable clearWindowHandler,
+ int currentOrientation, int activityType, Runnable clearWindowHandler,
ShellExecutor splashScreenExecutor) {
mSplashScreenExecutor = splashScreenExecutor;
mSession = WindowManagerGlobal.getWindowSession();
@@ -158,6 +173,7 @@ public class TaskSnapshotWindow {
int backgroundColor = taskDescription.getBackgroundColor();
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mOrientationOnCreation = currentOrientation;
+ mActivityType = activityType;
mClearWindowHandler = clearWindowHandler;
mHasImeSurface = snapshot.hasImeSurface();
}
@@ -170,7 +186,23 @@ public class TaskSnapshotWindow {
return mHasImeSurface;
}
+ void scheduleRemove(boolean deferRemoveForIme) {
+ // Show the latest content as soon as possible for unlocking to home.
+ if (mActivityType == ACTIVITY_TYPE_HOME) {
+ removeImmediately();
+ return;
+ }
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
+ final long delayRemovalTime = mHasImeSurface && deferRemoveForIme
+ ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
+ : DELAY_REMOVAL_TIME_GENERAL;
+ mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Defer removing snapshot surface in %d", delayRemovalTime);
+ }
+
void removeImmediately() {
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
deleted file mode 100644
index 144547885501..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.startingsurface;
-
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.view.Display;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.window.SnapshotDrawerUtils;
-import android.window.StartingWindowInfo;
-import android.window.TaskSnapshot;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
-
-class WindowlessSnapshotWindowCreator {
- private static final int DEFAULT_FADEOUT_DURATION = 233;
- private final StartingSurfaceDrawer.StartingWindowRecordManager
- mStartingWindowRecordManager;
- private final DisplayManager mDisplayManager;
- private final Context mContext;
- private final SplashscreenContentDrawer mSplashscreenContentDrawer;
- private final TransactionPool mTransactionPool;
-
- WindowlessSnapshotWindowCreator(
- StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager,
- Context context,
- DisplayManager displayManager, SplashscreenContentDrawer splashscreenContentDrawer,
- TransactionPool transactionPool) {
- mStartingWindowRecordManager = startingWindowRecordManager;
- mContext = context;
- mDisplayManager = displayManager;
- mSplashscreenContentDrawer = splashscreenContentDrawer;
- mTransactionPool = transactionPool;
- }
-
- void makeTaskSnapshotWindow(StartingWindowInfo info, SurfaceControl rootSurface,
- TaskSnapshot snapshot, ShellExecutor removeExecutor) {
- final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
- final int taskId = runningTaskInfo.taskId;
- final String title = "Windowless Snapshot " + taskId;
- final WindowManager.LayoutParams lp = SnapshotDrawerUtils.createLayoutParameters(
- info, title, TYPE_APPLICATION_OVERLAY, snapshot.getHardwareBuffer().getFormat(),
- null /* token */);
- if (lp == null) {
- return;
- }
- final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId);
- final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
- new StartingSurfaceDrawer.WindowlessStartingWindow(
- runningTaskInfo.configuration, rootSurface);
- final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
- mContext, display, wlw, "WindowlessSnapshotWindowCreator");
- final Point taskSize = snapshot.getTaskSize();
- final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y);
- final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
- final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
- final FrameLayout rootLayout = new FrameLayout(
- mSplashscreenContentDrawer.createViewContextWrapper(mContext));
- mViewHost.setView(rootLayout, lp);
- SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot,
- snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
-
- final ActivityManager.TaskDescription taskDescription =
- SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
-
- final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface,
- taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
- runningTaskInfo.topActivityType, removeExecutor);
- mStartingWindowRecordManager.addRecord(taskId, record);
- info.notifyAddComplete(wlw.mChildSurface);
- }
-
- private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
- private SurfaceControlViewHost mViewHost;
- private SurfaceControl mChildSurface;
- private final boolean mHasImeSurface;
-
- SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface,
- int bgColor, boolean hasImeSurface, int activityType,
- ShellExecutor removeExecutor) {
- super(activityType, removeExecutor);
- mViewHost = viewHost;
- mChildSurface = childSurface;
- mBGColor = bgColor;
- mHasImeSurface = hasImeSurface;
- }
-
- @Override
- protected void removeImmediately() {
- super.removeImmediately();
- fadeoutThenRelease();
- }
-
- void fadeoutThenRelease() {
- final ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1f, 0f);
- fadeOutAnimator.setDuration(DEFAULT_FADEOUT_DURATION);
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
- fadeOutAnimator.addUpdateListener(animation -> {
- if (mChildSurface == null || !mChildSurface.isValid()) {
- fadeOutAnimator.cancel();
- return;
- }
- t.setAlpha(mChildSurface, (float) animation.getAnimatedValue());
- t.apply();
- });
-
- fadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (mChildSurface == null || !mChildSurface.isValid()) {
- fadeOutAnimator.cancel();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mTransactionPool.release(t);
- if (mChildSurface != null) {
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
- t.remove(mChildSurface).apply();
- mTransactionPool.release(t);
- mChildSurface = null;
- }
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
- }
- });
- fadeOutAnimator.start();
- }
-
- @Override
- protected boolean hasImeSurface() {
- return mHasImeSurface;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
deleted file mode 100644
index 12a0d4054b4d..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.startingsurface;
-
-import static android.graphics.Color.WHITE;
-import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.os.Binder;
-import android.os.SystemClock;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.window.SplashScreenView;
-import android.window.StartingWindowInfo;
-import android.window.StartingWindowRemovalInfo;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
-
-class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
-
- private final TransactionPool mTransactionPool;
-
- WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer,
- Context context,
- ShellExecutor splashScreenExecutor,
- DisplayManager displayManager,
- StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager,
- TransactionPool pool) {
- super(contentDrawer, context, splashScreenExecutor, displayManager,
- startingWindowRecordManager);
- mTransactionPool = pool;
- }
-
- void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) {
- final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
- final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
- ? windowInfo.targetActivityInfo
- : taskInfo.topActivityInfo;
- if (activityInfo == null || activityInfo.packageName == null) {
- return;
- }
-
- final int displayId = taskInfo.displayId;
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- // Can't show splash screen on requested display, so skip showing at all.
- return;
- }
- final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo,
- 0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager);
- if (myContext == null) {
- return;
- }
- final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
- new StartingSurfaceDrawer.WindowlessStartingWindow(
- taskInfo.configuration, rootSurface);
- final SurfaceControlViewHost viewHost = new SurfaceControlViewHost(
- myContext, display, wlw, "WindowlessSplashWindowCreator");
- final String title = "Windowless Splash " + taskInfo.taskId;
- final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters(
- myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title,
- PixelFormat.TRANSLUCENT, new Binder());
- final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds();
- lp.width = windowBounds.width();
- lp.height = windowBounds.height();
- final ActivityManager.TaskDescription taskDescription;
- if (taskInfo.taskDescription != null) {
- taskDescription = taskInfo.taskDescription;
- } else {
- taskDescription = new ActivityManager.TaskDescription();
- taskDescription.setBackgroundColor(WHITE);
- }
-
- final FrameLayout rootLayout = new FrameLayout(
- mSplashscreenContentDrawer.createViewContextWrapper(mContext));
- viewHost.setView(rootLayout, lp);
-
- final int bgColor = taskDescription.getBackgroundColor();
- final SplashScreenView splashScreenView = mSplashscreenContentDrawer
- .makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor);
- rootLayout.addView(splashScreenView);
- final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView,
- wlw.mChildSurface, bgColor);
- mStartingWindowRecordManager.addRecord(taskInfo.taskId, record);
- windowInfo.notifyAddComplete(wlw.mChildSurface);
- }
-
- private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord {
- private SurfaceControlViewHost mViewHost;
- private final long mCreateTime;
- private SurfaceControl mChildSurface;
- private final SplashScreenView mSplashView;
-
- SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView,
- SurfaceControl childSurface, int bgColor) {
- mViewHost = viewHost;
- mSplashView = splashView;
- mChildSurface = childSurface;
- mBGColor = bgColor;
- mCreateTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
- if (!immediately) {
- mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
- info.windowAnimationLeash, info.mainFrame,
- this::release, mCreateTime, 0 /* roundedCornerRadius */);
- } else {
- release();
- }
- }
-
- void release() {
- if (mChildSurface != null) {
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
- t.remove(mChildSurface).apply();
- mTransactionPool.release(t);
- mChildSurface = null;
- }
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686f648..bb43d7c1a090 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -22,7 +22,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
@@ -31,7 +30,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -57,7 +55,6 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
- final boolean windowlessSurface = (parameter & TYPE_PARAMETER_WINDOWLESS) != 0;
final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
@@ -70,15 +67,10 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
+ "isSolidColorSplashScreen=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
- + "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
- topIsHome);
+ isSolidColorSplashScreen, legacySplashScreen, activityDrawn, topIsHome);
- if (windowlessSurface) {
- return STARTING_WINDOW_TYPE_WINDOWLESS;
- }
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
index 19133e29de4b..628ce27fe514 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -28,6 +28,7 @@ import android.window.WindowContainerToken;
import androidx.annotation.NonNull;
import com.android.wm.shell.util.CounterRotator;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.List;
@@ -57,7 +58,7 @@ public class CounterRotatorHelper {
for (int i = numChanges - 1; i >= 0; --i) {
final TransitionInfo.Change change = changes.get(i);
final WindowContainerToken parent = change.getParent();
- if (!Transitions.isClosingType(change.getMode())
+ if (!TransitionUtil.isClosingType(change.getMode())
|| !TransitionInfo.isIndependent(change, info) || parent == null) {
continue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 8e916e63cac6..ef405c858e3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
import java.util.Optional;
@@ -149,7 +150,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mSplitHandler.addEnterOrExitIfNeeded(request, out);
return out;
} else if (request.getRemoteTransition() != null
- && Transitions.isOpeningType(request.getType())
+ && TransitionUtil.isOpeningType(request.getType())
&& (request.getTriggerTask() == null
|| (request.getTriggerTask().topActivityType != ACTIVITY_TYPE_HOME
&& request.getTriggerTask().topActivityType != ACTIVITY_TYPE_RECENTS))) {
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 a3e05f2cf859..f66c26bb87e4 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
@@ -112,6 +112,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
import java.util.List;
@@ -445,7 +446,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
backgroundColorForTransition);
if (!isTask && a.hasExtension()) {
- if (!Transitions.isOpeningType(change.getMode())) {
+ if (!TransitionUtil.isOpeningType(change.getMode())) {
// Can screenshot now (before startTransaction is applied)
edgeExtendWindow(change, a, startTransaction, finishTransaction);
} else {
@@ -456,7 +457,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
- final Rect clipRect = Transitions.isClosingType(change.getMode())
+ final Rect clipRect = TransitionUtil.isClosingType(change.getMode())
? new Rect(mRotator.getEndBoundsInStartRotation(change))
: new Rect(change.getEndAbsBounds());
clipRect.offsetTo(0, 0);
@@ -562,12 +563,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
- final boolean isOpeningType = Transitions.isOpeningType(type);
- final boolean enter = Transitions.isOpeningType(changeMode);
+ final boolean isOpeningType = TransitionUtil.isOpeningType(type);
+ final boolean enter = TransitionUtil.isOpeningType(changeMode);
final boolean isTask = change.getTaskInfo() != null;
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
- final Rect endBounds = Transitions.isClosingType(changeMode)
+ final Rect endBounds = TransitionUtil.isClosingType(changeMode)
? mRotator.getEndBoundsInStartRotation(change)
: change.getEndAbsBounds();
@@ -689,8 +690,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
TransitionInfo.AnimationOptions options, float cornerRadius) {
- final boolean isOpen = Transitions.isOpeningType(change.getMode());
- final boolean isClose = Transitions.isClosingType(change.getMode());
+ final boolean isOpen = TransitionUtil.isOpeningType(change.getMode());
+ final boolean isClose = TransitionUtil.isClosingType(change.getMode());
if (isOpen) {
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
attachCrossProfileThumbnailAnimation(animations, finishCallback, change,
@@ -772,16 +773,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
- if (Transitions.isOpeningType(change.getMode())) {
+ if (TransitionUtil.isOpeningType(change.getMode())) {
hasOpenWallpaper = true;
- } else if (Transitions.isClosingType(change.getMode())) {
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
hasCloseWallpaper = true;
}
}
}
if (hasOpenWallpaper && hasCloseWallpaper) {
- return Transitions.isOpeningType(info.getType())
+ return TransitionUtil.isOpeningType(info.getType())
? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
} else if (hasOpenWallpaper) {
return WALLPAPER_TRANSITION_OPEN;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 02f19ebdb758..3c4e8898f215 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -39,6 +39,7 @@ import androidx.annotation.BinderThread;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
@@ -93,7 +94,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (!Transitions.SHELL_TRANSITIONS_ROTATION && Transitions.hasDisplayChange(info)) {
+ if (!Transitions.SHELL_TRANSITIONS_ROTATION && TransitionUtil.hasDisplayChange(info)) {
// Note that if the remote doesn't have permission ACCESS_SURFACE_FLINGER, some
// operations of the start transaction may be ignored.
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 5a5ceab383e7..8d29901c3a07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -54,6 +54,7 @@ import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.util.TransitionUtil;
/** The helper class that provides methods for adding styles to transition animations. */
public class TransitionAnimationHelper {
@@ -66,7 +67,7 @@ public class TransitionAnimationHelper {
final int type = info.getType();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
- final boolean enter = Transitions.isOpeningType(changeMode);
+ final boolean enter = TransitionUtil.isOpeningType(changeMode);
final boolean isTask = change.getTaskInfo() != null;
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 0a67477212e9..3b154d1cda83 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -19,13 +19,11 @@ package com.android.wm.shell.transition;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
-import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
@@ -33,6 +31,8 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
+import static com.android.wm.shell.util.TransitionUtil.isClosingType;
+import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -319,29 +319,6 @@ public class Transitions implements RemoteCallable<Transitions> {
}
}
- /** @return true if the transition was triggered by opening something vs closing something */
- public static boolean isOpeningType(@WindowManager.TransitionType int type) {
- return type == TRANSIT_OPEN
- || type == TRANSIT_TO_FRONT
- || type == TRANSIT_KEYGUARD_GOING_AWAY;
- }
-
- /** @return true if the transition was triggered by closing something vs opening something */
- public static boolean isClosingType(@WindowManager.TransitionType int type) {
- return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
- }
-
- /** Returns {@code true} if the transition has a display change. */
- public static boolean hasDisplayChange(@NonNull TransitionInfo info) {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getMode() == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) {
- return true;
- }
- }
- return false;
- }
-
/**
* Sets up visibility/alpha/transforms to resemble the starting state of an animation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
new file mode 100644
index 000000000000..145f759d4de2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+
+import android.annotation.NonNull;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+
+/** Various utility functions for transitions. */
+public class TransitionUtil {
+
+ /** @return true if the transition was triggered by opening something vs closing something */
+ public static boolean isOpeningType(@WindowManager.TransitionType int type) {
+ return type == TRANSIT_OPEN
+ || type == TRANSIT_TO_FRONT
+ || type == TRANSIT_KEYGUARD_GOING_AWAY;
+ }
+
+ /** @return true if the transition was triggered by closing something vs opening something */
+ public static boolean isClosingType(@WindowManager.TransitionType int type) {
+ return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
+ }
+
+ /** Returns {@code true} if the transition has a display change. */
+ public static boolean hasDisplayChange(@NonNull TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index d6adaa7d533b..b6696c70dbb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -41,6 +41,8 @@ android_test {
static_libs: [
"androidx.test.ext.junit",
"flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
"launcher-helper-lib",
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 65923ff36fc8..67ca9a1a17f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -24,7 +24,11 @@
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="run-command" value="settings put system show_touches 1" />
+ <option name="run-command" value="settings put system pointer_location 1" />
<option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
+ <option name="teardown-command" value="settings delete system show_touches" />
+ <option name="teardown-command" value="settings delete system pointer_location" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index aafd7edc7ef8..c5ee7b722617 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -18,12 +18,13 @@ package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.junit.FlickerBuilderProvider
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -32,7 +33,6 @@ import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Assume
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index bd18108c841e..ed93045ec462 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,13 +18,13 @@
package com.android.wm.shell.flicker
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
-import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
-import com.android.server.wm.traces.common.region.Region
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.common.datatypes.Region
+import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.helpers.WindowUtils
fun FlickerTest.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
@@ -247,7 +247,7 @@ fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean,
- rotation: PlatformConsts.Rotation
+ rotation: Rotation
): LayersTraceSubject {
return invoke("splitAppLayerBoundsSnapToDivider") {
it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation)
@@ -258,11 +258,13 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean,
- rotation: PlatformConsts.Rotation
+ rotation: Rotation
): LayerTraceEntrySubject {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return invoke {
- val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ val dividerRegion =
+ layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region
+ ?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found")
visibleRegion(component)
.coversAtMost(
if (displayBounds.width > displayBounds.height) {
@@ -367,46 +369,54 @@ fun FlickerTest.dockedStackDividerNotExistsAtEnd() {
}
fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
- rotation: PlatformConsts.Rotation,
+ rotation: Rotation,
primaryComponent: IComponentMatcher
) {
assertLayersEnd {
- val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ val dividerRegion =
+ layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region
+ ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found")
visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
}
}
fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
- rotation: PlatformConsts.Rotation,
+ rotation: Rotation,
primaryComponent: IComponentMatcher
) {
assertLayersEnd {
- val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ val dividerRegion =
+ layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region
+ ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found")
visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
}
}
fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
- rotation: PlatformConsts.Rotation,
+ rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
assertLayersEnd {
- val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ val dividerRegion =
+ layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region
+ ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found")
visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
}
}
fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
- rotation: PlatformConsts.Rotation,
+ rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
assertLayersEnd {
- val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ val dividerRegion =
+ layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region
+ ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found")
visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
}
}
-fun getPrimaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region {
+fun getPrimaryRegion(dividerRegion: Region, rotation: Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation.isRotated()) {
Region.from(
@@ -425,7 +435,7 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation):
}
}
-fun getSecondaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region {
+fun getSecondaryRegion(dividerRegion: Region, rotation: Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation.isRotated()) {
Region.from(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index e9c805ee5f4d..983640a70c4b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -18,7 +18,7 @@
package com.android.wm.shell.flicker
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
+import android.tools.common.datatypes.component.ComponentNameMatcher
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
const val LAUNCHER_UI_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 996b677470fe..bab81d79c804 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -21,16 +21,16 @@ import android.app.NotificationManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.ServiceManager
+import android.tools.common.Rotation
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.IFlickerTestData
import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
-import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
@@ -89,7 +89,7 @@ abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) {
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index 7358da3a58af..d0bca1332553 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -18,14 +18,14 @@ package com.android.wm.shell.flicker.bubble
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
index 1a0fbe461444..bdfdad59c600 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.wm.shell.flicker.bubble
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt
index cf696c8bbf59..5e85eb87e0e9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 9367a8a0491a..8474ce0e64e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.bubble
import android.content.Context
import android.graphics.Point
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
index 85a534c30ed5..62fa7b4516c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.wm.shell.flicker.bubble
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 0b1382be0c91..416315e4b06d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -18,14 +18,14 @@ package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import org.junit.Assume
@@ -36,7 +36,7 @@ import org.junit.runners.Parameterized
/**
* Test launching a new activity from bubble.
*
- * To run this test: `atest WMShellFlickerTests:LaunchBubbleFromLockScreen`
+ * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest`
*
* Actions:
* ```
@@ -46,7 +46,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleOnLockreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index 50507bf88d2f..07ba41333071 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
index 94147e876372..6c61710d6284 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.wm.shell.flicker.bubble
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 4be4dcd7e1f0..29f76d01af83 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
index 7efbcdbf0013..e323ebf3b5c8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.wm.shell.flicker.bubble
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 88cf15e92c99..1045a5ac2ce8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -18,10 +18,10 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -44,7 +44,7 @@ import org.junit.runners.Parameterized
* ```
* 1. All assertions are inherited from [EnterPipTest]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 88542d51563d..2d2588ef4348 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,7 +43,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
index fb1eb01918d7..02f60100d069 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,7 +40,7 @@ class ClosePipBySwipingDownTestCfArm(flicker: FlickerTest) : ClosePipBySwipingDo
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
index 080e033f3074..6c5a344c8f79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
@@ -17,13 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.LAUNCHER
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Test
import org.junit.runners.Parameterized
@@ -32,7 +32,7 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker)
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup { this.setRotation(flicker.scenario.startRotation) }
- teardown { this.setRotation(PlatformConsts.Rotation.ROTATION_0) }
+ teardown { this.setRotation(Rotation.ROTATION_0) }
}
/**
@@ -91,7 +91,7 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker)
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index f27fa4a81328..e540ad543228 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,7 +43,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
index fbada69f6f32..05262feceba5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -41,7 +41,7 @@ open class ClosePipWithDismissButtonTestCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 47537c6c5cdd..11bb0cc1306e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -39,14 +39,6 @@ import org.junit.runners.Parameterized
* Select "Via code behind" radio button
* Press Home button or swipe up to go Home and put [pipApp] in pip mode
* ```
- * Notes:
- * ```
- * 1. All assertions are inherited from [EnterPipTest]
- * 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
- * including configuring navigation mode, initial orientation and ensuring no
- * apps are running before setup
- * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
index e47805001cd0..90f99c0c4cae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index db5048968112..e079d5477e2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -20,18 +20,18 @@ import android.app.Activity
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
import org.junit.Assume
@@ -58,7 +58,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
@@ -69,8 +69,8 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flicker) {
private val testApp = FixedOrientationAppHelper(instrumentation)
- private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90)
- private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0)
+ private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
+ private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -213,7 +213,7 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
index ec5f13cbed49..58416660826f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -42,7 +42,7 @@ open class EnterPipToOtherOrientationCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index 3ef66d7e8ed2..327225421580 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.Test
import org.junit.runners.Parameterized
@@ -130,7 +130,7 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker)
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index c3c705eb58e5..1f060e931be2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,7 +40,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
index b487ff4a296b..4390f0bb70b2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,7 +40,7 @@ class EnterPipViaAppUiButtonTestCfArm(flicker: FlickerTest) : EnterPipViaAppUiBu
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index f88f8d6e64ed..2001f484ed96 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Test
import org.junit.runners.Parameterized
@@ -137,7 +137,7 @@ abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flic
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index d2fbb2a2c941..313631cbe8ee 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -47,7 +47,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
index 8b3755e38366..eccb85d98798 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -41,7 +41,7 @@ class ExitPipToAppViaExpandButtonTestCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index a9eb18d44856..93ffdd8d5294 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -46,7 +46,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
index 39b1c82f9676..6ab6a1f0bb73 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,7 +40,7 @@ class ExitPipToAppViaIntentTestCfArm(flicker: FlickerTest) : ExitPipToAppViaInte
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index d577b4f46319..7d5f740838bd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -17,13 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,7 +45,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
@@ -147,7 +147,7 @@ open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flic
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
index 08db8aefb148..c09623490041 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -41,7 +41,7 @@ class ExpandPipOnDoubleClickTestTestCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index fcb8af4e8d40..0b73aac02797 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,7 +61,7 @@ open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicke
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
index 30050bf4e9d9..e064bf2ee921 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,7 +40,7 @@ class ExpandPipOnPinchOpenTestCfArm(flicker: FlickerTest) : ExpandPipOnPinchOpen
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 39ac49f8c81c..9c007449fb8d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
import org.junit.Test
@@ -45,7 +45,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index 7db80a8c8110..c23838a987bf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -17,17 +17,17 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
@@ -91,7 +91,7 @@ open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransitio
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
index be3bd60d28e8..d3d77d20662e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -37,7 +37,7 @@ class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt
index ef9920c3c793..6f8111690f0f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 77a8c3c3e43f..109354ab5c79 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
+import android.tools.common.Rotation
+import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.Direction
import org.junit.Test
import org.junit.runners.Parameterized
@@ -118,7 +118,7 @@ abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransitio
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 511a6511eb44..c8d5624b1d77 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
import org.junit.Test
@@ -45,7 +45,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index e13344390584..85b2fbce2f21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,7 +61,7 @@ class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 166416a49e4b..b30f30830156 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.pip
import android.app.Instrumentation
import android.content.Intent
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import android.tools.device.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.PipAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.BaseTest
import com.google.common.truth.Truth
import org.junit.Test
@@ -70,7 +70,7 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
): FlickerBuilder.() -> Unit {
return {
setup {
- setRotation(PlatformConsts.Rotation.ROTATION_0)
+ setRotation(Rotation.ROTATION_0)
removeAllTasksButHome()
pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index 3f5d06748d05..3850c1f6c89a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -20,15 +20,15 @@ import android.app.Activity
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import org.junit.Assume
import org.junit.Before
@@ -47,8 +47,8 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransition(flicker) {
- private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0)
- private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90)
+ private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
+ private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -66,7 +66,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
wmHelper
.StateSyncBuilder()
.withPipShown()
- .withRotation(PlatformConsts.Rotation.ROTATION_0)
+ .withRotation(Rotation.ROTATION_0)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
@@ -79,7 +79,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
wmHelper
.StateSyncBuilder()
.withFullScreenApp(pipApp)
- .withRotation(PlatformConsts.Rotation.ROTATION_90)
+ .withRotation(Rotation.ROTATION_90)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
@@ -98,7 +98,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
@Presubmit
@Test
fun displayEndsAt90Degrees() {
- flicker.assertWmEnd { hasRotation(PlatformConsts.Rotation.ROTATION_90) }
+ flicker.assertWmEnd { hasRotation(Rotation.ROTATION_90) }
}
@Presubmit
@@ -151,7 +151,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 720fe7244047..2cf8f61f13fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -17,14 +17,14 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,7 +48,7 @@ import org.junit.runners.Parameterized
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
index daf3e1b18b4b..b7a2c47e3b32 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.pip
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
index 36909dd74245..000ae8f9458e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
@@ -17,12 +17,12 @@
package com.android.wm.shell.flicker.pip.tv
import android.app.Instrumentation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.PipAppHelper
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
/** Helper class for PIP app on AndroidTV */
open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index dc1fe4761757..6104b7bdacba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -20,10 +20,10 @@ import android.app.ActivityManager
import android.app.IActivityManager
import android.app.IProcessObserver
import android.os.SystemClock
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.view.Surface.ROTATION_0
import android.view.Surface.rotationToString
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.After
import org.junit.Assert.assertFalse
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 247403a2cbc6..0c9c16153ea3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.datatypes.component.EdgeExtensionComponentMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.component.matchers.EdgeExtensionComponentMatcher
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index d3c68207bf97..1b55f3975e1c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -20,12 +20,12 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesInvisible
@@ -104,14 +104,12 @@ class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicke
@Test
fun secondaryAppBoundsIsFullscreenAtEnd() {
flicker.assertLayers {
- this.isVisible(secondaryApp)
- .then()
- .isInvisible(secondaryApp)
- .then()
- .invoke("secondaryAppBoundsIsFullscreenAtEnd") {
- val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
- it.visibleRegion(secondaryApp).coversExactly(displayBounds)
- }
+ this.isVisible(secondaryApp).then().isInvisible(secondaryApp).then().invoke(
+ "secondaryAppBoundsIsFullscreenAtEnd"
+ ) {
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
+ it.visibleRegion(secondaryApp).coversExactly(displayBounds)
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index b44b681704ba..bd2ffc1a018d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 514365fbd71a..7db5ecc484ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 4e36c367f226..ffdb87f190d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -186,7 +186,7 @@ class EnterSplitScreenByDragFromAllApps(flicker: FlickerTest) : SplitScreenBase(
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 5d37e858c15f..792e2b03522f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesVisible
@@ -208,7 +208,7 @@ class EnterSplitScreenByDragFromNotification(flicker: FlickerTest) : SplitScreen
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index d086f7e04486..c1977e9e82f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
@@ -136,7 +136,7 @@ class EnterSplitScreenByDragFromShortcut(flicker: FlickerTest) : SplitScreenBase
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 795a2c4f43ba..da80c6f46976 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -205,7 +205,7 @@ class EnterSplitScreenByDragFromTaskbar(flicker: FlickerTest) : SplitScreenBase(
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index a9cbb7419417..c45387722a49 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 8c0a303189e1..7abdc06820d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -17,8 +17,8 @@
package com.android.wm.shell.flicker.splitscreen
import android.content.Context
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
index 4f8cfca5c872..7901f7502e2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -19,6 +19,12 @@ package com.android.wm.shell.flicker.splitscreen
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.common.datatypes.component.IComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import android.view.InputDevice
import android.view.MotionEvent
import android.view.ViewConfiguration
@@ -32,16 +38,9 @@ import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
-import com.android.server.wm.traces.common.component.matchers.IComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import java.util.Collections
import org.junit.Assert.assertNotNull
internal object SplitScreenUtils {
@@ -129,18 +128,12 @@ internal object SplitScreenUtils {
// Find the second task in the upper right corner in split select mode by sorting
// 'left' in descending order and 'top' in ascending order.
- Collections.sort(
- snapshots,
- { t1: UiObject2, t2: UiObject2 ->
- t2.getVisibleBounds().left - t1.getVisibleBounds().left
- }
- )
- Collections.sort(
- snapshots,
- { t1: UiObject2, t2: UiObject2 ->
- t1.getVisibleBounds().top - t2.getVisibleBounds().top
- }
- )
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t2.getVisibleBounds().left - t1.getVisibleBounds().left
+ }
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ }
snapshots[0].click()
} else {
tapl.workspace
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index c7b81d924a9b..fbb7c7159234 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -19,14 +19,15 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
@@ -133,7 +134,7 @@ class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicke
.waitForAndVerify()
}
- private fun isLandscape(rotation: PlatformConsts.Rotation): Boolean {
+ private fun isLandscape(rotation: Rotation): Boolean {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return displayBounds.width > displayBounds.height
}
@@ -205,7 +206,7 @@ class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicke
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 940e0e93d524..d675bfb0119d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
@@ -166,7 +166,7 @@ class SwitchBackToSplitFromAnotherApp(flicker: FlickerTest) : SplitScreenBase(fl
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 8c3bea8e6297..2855c71518eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
@@ -179,7 +179,7 @@ class SwitchBackToSplitFromHome(flicker: FlickerTest) : SplitScreenBase(flicker)
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 06a1449f75ee..c29a917c4e7c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
@@ -179,7 +179,7 @@ class SwitchBackToSplitFromRecent(flicker: FlickerTest) : SplitScreenBase(flicke
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 193ab98cf191..4c96b3a319d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.appWindowBecomesVisible
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index bf62acfc47a1..11fda8bf7bbc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -24,8 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MAX_ANIMATION_DURATION;
-import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MINIMAL_ANIMATION_DURATION;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -56,9 +56,11 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.TestableContext;
+import android.view.Display;
import android.view.IWindowSession;
import android.view.InsetsState;
import android.view.Surface;
+import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
@@ -104,7 +106,36 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
private ShellExecutor mTestExecutor;
private final TestableContext mTestContext = new TestContext(
InstrumentationRegistry.getInstrumentation().getTargetContext());
- StartingSurfaceDrawer mStartingSurfaceDrawer;
+ TestStartingSurfaceDrawer mStartingSurfaceDrawer;
+
+ static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{
+ int mAddWindowForTask = 0;
+
+ TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
+ IconProvider iconProvider, TransactionPool pool) {
+ super(context, splashScreenExecutor, iconProvider, pool);
+ }
+
+ @Override
+ protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
+ WindowManager.LayoutParams params, int suggestType) {
+ // listen for addView
+ mAddWindowForTask = taskId;
+ saveSplashScreenRecord(appToken, taskId, view, suggestType);
+ // Do not wait for background color
+ return false;
+ }
+
+ @Override
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo,
+ boolean immediately) {
+ // listen for removeView
+ if (mAddWindowForTask == removalInfo.taskId) {
+ mAddWindowForTask = 0;
+ }
+ mStartingWindowRecords.remove(removalInfo.taskId);
+ }
+ }
private static class TestContext extends TestableContext {
TestContext(Context context) {
@@ -134,51 +165,44 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
doNothing().when(mMockWindowManager).addView(any(), any());
mTestExecutor = new HandlerExecutor(mTestHandler);
- mStartingSurfaceDrawer = new StartingSurfaceDrawer(mTestContext, mTestExecutor,
- mIconProvider, mTransactionPool);
mStartingSurfaceDrawer = spy(
- new StartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider,
+ new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider,
mTransactionPool));
- spyOn(mStartingSurfaceDrawer.mSplashscreenWindowCreator);
- spyOn(mStartingSurfaceDrawer.mWindowRecords);
- spyOn(mStartingSurfaceDrawer.mWindowlessRecords);
}
@Test
public void testAddSplashScreenSurface() {
final int taskId = 1;
final StartingWindowInfo windowInfo =
- createWindowInfo(taskId, android.R.style.Theme, mBinder);
- mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo,
+ createWindowInfo(taskId, android.R.style.Theme);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator).addWindow(
- eq(taskId), eq(mBinder), any(), any(), any(),
+ verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
+ assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
removalInfo.taskId = windowInfo.taskInfo.taskId;
mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(false));
- assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0);
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false));
+ assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
@Test
public void testFallbackDefaultTheme() {
final int taskId = 1;
final StartingWindowInfo windowInfo =
- createWindowInfo(taskId, 0, mBinder);
+ createWindowInfo(taskId, 0);
final int[] theme = new int[1];
doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod())
- .when(mStartingSurfaceDrawer.mSplashscreenWindowCreator)
- .getSplashScreenTheme(eq(0), any());
+ .when(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any());
- mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo,
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator)
- .getSplashScreenTheme(eq(0), any());
+ verify(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any());
assertNotEquals(theme[0], 0);
}
@@ -217,7 +241,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception {
final int taskId = 1;
final StartingWindowInfo windowInfo =
- createWindowInfo(taskId, android.R.style.Theme, mBinder);
+ createWindowInfo(taskId, android.R.style.Theme);
TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100),
new Rect(0, 0, 0, 50), true /* hasImeSurface */);
final IWindowSession session = WindowManagerGlobal.getWindowSession();
@@ -246,7 +270,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
any())).thenReturn(mockSnapshotWindow);
// Simulate a task snapshot window created with IME snapshot shown.
- mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot);
waitHandlerIdle(mTestHandler);
// Verify the task snapshot with IME snapshot will be removed when received the real IME
@@ -254,36 +278,27 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
// makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
// StartingWindowRecord for the task.
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
- verify(mStartingSurfaceDrawer.mWindowRecords, times(2))
- .removeWindow(any(), eq(true));
+ verify(mStartingSurfaceDrawer, times(2))
+ .removeWindowSynced(any(), eq(true));
}
}
@Test
public void testClearAllWindows() {
final int taskId = 1;
- mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId,
- new StartingSurfaceDrawer.StartingWindowRecord() {
- @Override
- public void removeIfPossible(StartingWindowRemovalInfo info,
- boolean immediately) {
-
- }
- });
- mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId,
- new StartingSurfaceDrawer.StartingWindowRecord() {
- @Override
- public void removeIfPossible(StartingWindowRemovalInfo info,
- boolean immediately) {
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, android.R.style.Theme);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
+ STARTING_WINDOW_TYPE_SPLASH_SCREEN);
+ waitHandlerIdle(mTestHandler);
+ verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
+ eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
+ assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
- }
- });
mStartingSurfaceDrawer.clearAllWindows();
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(true));
- assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0);
- verify(mStartingSurfaceDrawer.mWindowlessRecords).removeWindow(any(), eq(true));
- assertEquals(mStartingSurfaceDrawer.mWindowlessRecords.recordSize(), 0);
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true));
+ assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
}
@Test
@@ -336,7 +351,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
longAppDuration, longAppDuration));
}
- private StartingWindowInfo createWindowInfo(int taskId, int themeResId, IBinder appToken) {
+ private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
@@ -345,7 +360,6 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.topActivityInfo = info;
taskInfo.taskId = taskId;
- windowInfo.appToken = appToken;
windowInfo.targetActivityInfo = info;
windowInfo.taskInfo = taskInfo;
windowInfo.topOpaqueWindowInsetsState = new InsetsState();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index bcbe706d71a3..536bb49675f1 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -332,6 +332,7 @@ cc_defaults {
"jni/android_graphics_Matrix.cpp",
"jni/android_graphics_Picture.cpp",
"jni/android_graphics_DisplayListCanvas.cpp",
+ "jni/android_graphics_Mesh.cpp",
"jni/android_graphics_RenderNode.cpp",
"jni/android_nio_utils.cpp",
"jni/android_util_PathParser.cpp",
@@ -351,7 +352,6 @@ cc_defaults {
"jni/ImageDecoder.cpp",
"jni/Interpolator.cpp",
"jni/MeshSpecification.cpp",
- "jni/Mesh.cpp",
"jni/MaskFilter.cpp",
"jni/NinePatch.cpp",
"jni/NinePatchPeeker.cpp",
@@ -538,6 +538,7 @@ cc_defaults {
"Interpolator.cpp",
"LightingInfo.cpp",
"Matrix.cpp",
+ "Mesh.cpp",
"MemoryPolicy.cpp",
"PathParser.cpp",
"Properties.cpp",
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index e2127efca716..a18ba1c633b9 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -52,4 +52,5 @@ X(DrawShadowRec)
X(DrawVectorDrawable)
X(DrawRippleDrawable)
X(DrawWebView)
-X(DrawMesh)
+X(DrawSkMesh)
+X(DrawMesh) \ No newline at end of file
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
index 2f0f7f506447..41ced8cebf83 100644
--- a/libs/hwui/MemoryPolicy.h
+++ b/libs/hwui/MemoryPolicy.h
@@ -53,8 +53,8 @@ struct MemoryPolicy {
// Whether or not to only purge scratch resources when triggering UI Hidden or background
// collection
bool purgeScratchOnly = true;
- // Whether or not to trigger releasing GPU context when all contexts are stopped
- bool releaseContextOnStoppedOnly = true;
+ // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
+ bool releaseContextOnStoppedOnly = false;
};
const MemoryPolicy& loadMemoryPolicy();
diff --git a/libs/hwui/Mesh.cpp b/libs/hwui/Mesh.cpp
new file mode 100644
index 000000000000..e59bc9565a59
--- /dev/null
+++ b/libs/hwui/Mesh.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#include "Mesh.h"
+
+#include <GLES/gl.h>
+#include <SkMesh.h>
+
+#include "SafeMath.h"
+
+static size_t min_vcount_for_mode(SkMesh::Mode mode) {
+ switch (mode) {
+ case SkMesh::Mode::kTriangles:
+ return 3;
+ case SkMesh::Mode::kTriangleStrip:
+ return 3;
+ }
+}
+
+// Re-implementation of SkMesh::validate to validate user side that their mesh is valid.
+std::tuple<bool, SkString> Mesh::validate() {
+#define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__))
+ if (!mMeshSpec) {
+ FAIL_MESH_VALIDATE("MeshSpecification is required.");
+ }
+ if (mVertexBufferData.empty()) {
+ FAIL_MESH_VALIDATE("VertexBuffer is required.");
+ }
+
+ auto meshStride = mMeshSpec->stride();
+ auto meshMode = SkMesh::Mode(mMode);
+ SafeMath sm;
+ size_t vsize = sm.mul(meshStride, mVertexCount);
+ if (sm.add(vsize, mVertexOffset) > mVertexBufferData.size()) {
+ FAIL_MESH_VALIDATE(
+ "The vertex buffer offset and vertex count reads beyond the end of the"
+ " vertex buffer.");
+ }
+
+ if (mVertexOffset % meshStride != 0) {
+ FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).",
+ mVertexOffset, meshStride);
+ }
+
+ if (size_t uniformSize = mMeshSpec->uniformSize()) {
+ if (!mBuilder->fUniforms || mBuilder->fUniforms->size() < uniformSize) {
+ FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.",
+ mBuilder->fUniforms->size(), uniformSize);
+ }
+ }
+
+ auto modeToStr = [](SkMesh::Mode m) {
+ switch (m) {
+ case SkMesh::Mode::kTriangles:
+ return "triangles";
+ case SkMesh::Mode::kTriangleStrip:
+ return "triangle-strip";
+ }
+ };
+ if (!mIndexBufferData.empty()) {
+ if (mIndexCount < min_vcount_for_mode(meshMode)) {
+ FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.",
+ modeToStr(meshMode), min_vcount_for_mode(meshMode), mIndexCount);
+ }
+ size_t isize = sm.mul(sizeof(uint16_t), mIndexCount);
+ if (sm.add(isize, mIndexOffset) > mIndexBufferData.size()) {
+ FAIL_MESH_VALIDATE(
+ "The index buffer offset and index count reads beyond the end of the"
+ " index buffer.");
+ }
+ // If we allow 32 bit indices then this should enforce 4 byte alignment in that case.
+ if (!SkIsAlign2(mIndexOffset)) {
+ FAIL_MESH_VALIDATE("The index offset must be a multiple of 2.");
+ }
+ } else {
+ if (mVertexCount < min_vcount_for_mode(meshMode)) {
+ FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.",
+ modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount);
+ }
+ SkASSERT(!fICount);
+ SkASSERT(!fIOffset);
+ }
+
+ if (!sm.ok()) {
+ FAIL_MESH_VALIDATE("Overflow");
+ }
+#undef FAIL_MESH_VALIDATE
+ return {true, {}};
+}
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
new file mode 100644
index 000000000000..983681707415
--- /dev/null
+++ b/libs/hwui/Mesh.h
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#ifndef MESH_H_
+#define MESH_H_
+
+#include <GrDirectContext.h>
+#include <SkMesh.h>
+#include <jni.h>
+#include <log/log.h>
+
+#include <utility>
+
+class MeshUniformBuilder {
+public:
+ struct MeshUniform {
+ template <typename T>
+ std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
+ const T& val) {
+ if (!fVar) {
+ LOG_FATAL("Assigning to missing variable");
+ } else if (sizeof(val) != fVar->sizeInBytes()) {
+ LOG_FATAL("Incorrect value size");
+ } else {
+ void* dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+ memcpy(dst, &val, sizeof(val));
+ }
+ }
+
+ MeshUniform& operator=(const SkMatrix& val) {
+ if (!fVar) {
+ LOG_FATAL("Assigning to missing variable");
+ } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
+ LOG_FATAL("Incorrect value size");
+ } else {
+ float* data = reinterpret_cast<float*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+ data[0] = val.get(0);
+ data[1] = val.get(3);
+ data[2] = val.get(6);
+ data[3] = val.get(1);
+ data[4] = val.get(4);
+ data[5] = val.get(7);
+ data[6] = val.get(2);
+ data[7] = val.get(5);
+ data[8] = val.get(8);
+ }
+ return *this;
+ }
+
+ template <typename T>
+ bool set(const T val[], const int count) {
+ static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
+ if (!fVar) {
+ LOG_FATAL("Assigning to missing variable");
+ return false;
+ } else if (sizeof(T) * count != fVar->sizeInBytes()) {
+ LOG_FATAL("Incorrect value size");
+ return false;
+ } else {
+ void* dst = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+ memcpy(dst, val, sizeof(T) * count);
+ }
+ return true;
+ }
+
+ MeshUniformBuilder* fOwner;
+ const SkRuntimeEffect::Uniform* fVar;
+ };
+ MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; }
+
+ explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) {
+ fMeshSpec = sk_sp(meshSpec);
+ fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
+ }
+
+ sk_sp<SkData> fUniforms;
+
+private:
+ void* writableUniformData() {
+ if (!fUniforms->unique()) {
+ fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
+ }
+ return fUniforms->writable_data();
+ }
+
+ sk_sp<SkMeshSpecification> fMeshSpec;
+};
+
+class Mesh {
+public:
+ Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer,
+ size_t vertexBufferSize, jint vertexCount, jint vertexOffset,
+ std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
+ : mMeshSpec(meshSpec)
+ , mMode(mode)
+ , mVertexCount(vertexCount)
+ , mVertexOffset(vertexOffset)
+ , mBuilder(std::move(builder))
+ , mBounds(bounds) {
+ copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize);
+ }
+
+ Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer,
+ size_t vertexBufferSize, jint vertexCount, jint vertexOffset, const void* indexBuffer,
+ size_t indexBufferSize, jint indexCount, jint indexOffset,
+ std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
+ : mMeshSpec(meshSpec)
+ , mMode(mode)
+ , mVertexCount(vertexCount)
+ , mVertexOffset(vertexOffset)
+ , mIndexCount(indexCount)
+ , mIndexOffset(indexOffset)
+ , mBuilder(std::move(builder))
+ , mBounds(bounds) {
+ copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize);
+ copyToVector(mIndexBufferData, indexBuffer, indexBufferSize);
+ }
+
+ Mesh(Mesh&&) = default;
+
+ Mesh& operator=(Mesh&&) = default;
+
+ [[nodiscard]] std::tuple<bool, SkString> validate();
+
+ void updateSkMesh(GrDirectContext* context) const {
+ GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID();
+ if (context) {
+ genId = context->directContextID();
+ }
+
+ if (mIsDirty || genId != mGenerationId) {
+ auto vb = SkMesh::MakeVertexBuffer(
+ context, reinterpret_cast<const void*>(mVertexBufferData.data()),
+ mVertexBufferData.size());
+ auto meshMode = SkMesh::Mode(mMode);
+ if (!mIndexBufferData.empty()) {
+ auto ib = SkMesh::MakeIndexBuffer(
+ context, reinterpret_cast<const void*>(mIndexBufferData.data()),
+ mIndexBufferData.size());
+ mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
+ ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
+ mBounds)
+ .mesh;
+ } else {
+ mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
+ mBuilder->fUniforms, mBounds)
+ .mesh;
+ }
+ mIsDirty = false;
+ mGenerationId = genId;
+ }
+ }
+
+ SkMesh& getSkMesh() const {
+ LOG_FATAL_IF(mIsDirty,
+ "Attempt to obtain SkMesh when Mesh is dirty, did you "
+ "forget to call updateSkMesh with a GrDirectContext? "
+ "Defensively creating a CPU mesh");
+ return mMesh;
+ }
+
+ void markDirty() { mIsDirty = true; }
+
+ MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); }
+
+private:
+ void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) {
+ if (src) {
+ dst.resize(srcSize);
+ memcpy(dst.data(), src, srcSize);
+ }
+ }
+
+ sk_sp<SkMeshSpecification> mMeshSpec;
+ int mMode = 0;
+
+ std::vector<uint8_t> mVertexBufferData;
+ size_t mVertexCount = 0;
+ size_t mVertexOffset = 0;
+
+ std::vector<uint8_t> mIndexBufferData;
+ size_t mIndexCount = 0;
+ size_t mIndexOffset = 0;
+
+ std::unique_ptr<MeshUniformBuilder> mBuilder;
+ SkRect mBounds{};
+
+ mutable SkMesh mMesh{};
+ mutable bool mIsDirty = true;
+ mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID();
+};
+#endif // MESH_H_
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 659aec0fdf58..0b58406516e3 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -24,6 +24,7 @@
#include <experimental/type_traits>
#include <utility>
+#include "Mesh.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkBlendMode.h"
#include "SkCanvas.h"
@@ -502,14 +503,14 @@ struct DrawVertices final : Op {
c->drawVertices(vertices, mode, paint);
}
};
-struct DrawMesh final : Op {
- static const auto kType = Type::DrawMesh;
- DrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
+struct DrawSkMesh final : Op {
+ static const auto kType = Type::DrawSkMesh;
+ DrawSkMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
: cpuMesh(mesh), blender(std::move(blender)), paint(paint) {
isGpuBased = false;
}
- SkMesh cpuMesh;
+ const SkMesh& cpuMesh;
mutable SkMesh gpuMesh;
sk_sp<SkBlender> blender;
SkPaint paint;
@@ -517,6 +518,7 @@ struct DrawMesh final : Op {
mutable GrDirectContext::DirectContextID contextId;
void draw(SkCanvas* c, const SkMatrix&) const {
GrDirectContext* directContext = c->recordingContext()->asDirectContext();
+
GrDirectContext::DirectContextID id = directContext->directContextID();
if (!isGpuBased || contextId != id) {
sk_sp<SkMesh::VertexBuffer> vb =
@@ -543,6 +545,18 @@ struct DrawMesh final : Op {
c->drawMesh(gpuMesh, blender, paint);
}
};
+
+struct DrawMesh final : Op {
+ static const auto kType = Type::DrawMesh;
+ DrawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
+ : mesh(mesh), blender(std::move(blender)), paint(paint) {}
+
+ const Mesh& mesh;
+ sk_sp<SkBlender> blender;
+ SkPaint paint;
+
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh.getSkMesh(), blender, paint); }
+};
struct DrawAtlas final : Op {
static const auto kType = Type::DrawAtlas;
DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling,
@@ -859,6 +873,10 @@ void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, con
}
void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp<SkBlender>& blender,
const SkPaint& paint) {
+ this->push<DrawSkMesh>(0, mesh, blender, paint);
+}
+void DisplayListData::drawMesh(const Mesh& mesh, const sk_sp<SkBlender>& blender,
+ const SkPaint& paint) {
this->push<DrawMesh>(0, mesh, blender, paint);
}
void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
@@ -1205,6 +1223,9 @@ void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
const SkPaint& paint) {
fDL->drawMesh(mesh, blender, paint);
}
+void RecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
+ fDL->drawMesh(mesh, blender, paint);
+}
void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[],
const SkRect texs[], const SkColor colors[], int count,
SkBlendMode bmode, const SkSamplingOptions& sampling,
@@ -1223,5 +1244,14 @@ void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) {
fDL->drawWebView(drawable);
}
+[[nodiscard]] const SkMesh& DrawMeshPayload::getSkMesh() const {
+ LOG_FATAL_IF(!meshWrapper && !mesh, "One of Mesh or Mesh must be non-null");
+ if (meshWrapper) {
+ return meshWrapper->getSkMesh();
+ } else {
+ return *mesh;
+ }
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 8409e136b57b..1f4ba5d6d557 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -28,6 +28,7 @@
#include <log/log.h>
#include <cstdlib>
+#include <utility>
#include <vector>
#include "CanvasTransform.h"
@@ -40,6 +41,7 @@
enum class SkBlendMode;
class SkRRect;
+class Mesh;
namespace android {
namespace uirenderer {
@@ -66,6 +68,18 @@ struct DisplayListOp {
static_assert(sizeof(DisplayListOp) == 4);
+class DrawMeshPayload {
+public:
+ explicit DrawMeshPayload(const SkMesh* mesh) : mesh(mesh) {}
+ explicit DrawMeshPayload(const Mesh* meshWrapper) : meshWrapper(meshWrapper) {}
+
+ [[nodiscard]] const SkMesh& getSkMesh() const;
+
+private:
+ const SkMesh* mesh = nullptr;
+ const Mesh* meshWrapper = nullptr;
+};
+
struct DrawImagePayload {
explicit DrawImagePayload(Bitmap& bitmap)
: image(bitmap.makeImage()), palette(bitmap.palette()) {
@@ -143,6 +157,7 @@ private:
void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
void drawMesh(const SkMesh&, const sk_sp<SkBlender>&, const SkPaint&);
+ void drawMesh(const Mesh&, const sk_sp<SkBlender>&, const SkPaint&);
void drawAnnotation(const SkRect&, const char*, SkData*);
void drawDrawable(SkDrawable*, const SkMatrix*);
@@ -247,6 +262,7 @@ public:
SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
+ void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint);
void drawVectorDrawable(VectorDrawableRoot* tree);
void drawWebView(skiapipeline::FunctorDrawable*);
diff --git a/libs/hwui/SafeMath.h b/libs/hwui/SafeMath.h
new file mode 100644
index 000000000000..4d6adf55c0cb
--- /dev/null
+++ b/libs/hwui/SafeMath.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef SkSafeMath_DEFINED
+#define SkSafeMath_DEFINED
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+// Copy of Skia's SafeMath API used to validate Mesh parameters to support
+// deferred creation of SkMesh instances on RenderThread.
+// SafeMath always check that a series of operations do not overflow.
+// This must be correct for all platforms, because this is a check for safety at runtime.
+
+class SafeMath {
+public:
+ SafeMath() = default;
+
+ bool ok() const { return fOK; }
+ explicit operator bool() const { return fOK; }
+
+ size_t mul(size_t x, size_t y) {
+ return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y);
+ }
+
+ size_t add(size_t x, size_t y) {
+ size_t result = x + y;
+ fOK &= result >= x;
+ return result;
+ }
+
+ /**
+ * Return a + b, unless this result is an overflow/underflow. In those cases, fOK will
+ * be set to false, and it is undefined what this returns.
+ */
+ int addInt(int a, int b) {
+ if (b < 0 && a < std::numeric_limits<int>::min() - b) {
+ fOK = false;
+ return a;
+ } else if (b > 0 && a > std::numeric_limits<int>::max() - b) {
+ fOK = false;
+ return a;
+ }
+ return a + b;
+ }
+
+ // These saturate to their results
+ static size_t Add(size_t x, size_t y) {
+ SafeMath tmp;
+ size_t sum = tmp.add(x, y);
+ return tmp.ok() ? sum : SIZE_MAX;
+ }
+
+ static size_t Mul(size_t x, size_t y) {
+ SafeMath tmp;
+ size_t prod = tmp.mul(x, y);
+ return tmp.ok() ? prod : SIZE_MAX;
+ }
+
+private:
+ uint32_t mul32(uint32_t x, uint32_t y) {
+ uint64_t bx = x;
+ uint64_t by = y;
+ uint64_t result = bx * by;
+ fOK &= result >> 32 == 0;
+ // Overflow information is capture in fOK. Return the result modulo 2^32.
+ return (uint32_t)result;
+ }
+
+ uint64_t mul64(uint64_t x, uint64_t y) {
+ if (x <= std::numeric_limits<uint64_t>::max() >> 32 &&
+ y <= std::numeric_limits<uint64_t>::max() >> 32) {
+ return x * y;
+ } else {
+ auto hi = [](uint64_t x) { return x >> 32; };
+ auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
+
+ uint64_t lx_ly = lo(x) * lo(y);
+ uint64_t hx_ly = hi(x) * lo(y);
+ uint64_t lx_hy = lo(x) * hi(y);
+ uint64_t hx_hy = hi(x) * hi(y);
+ uint64_t result = 0;
+ result = this->add(lx_ly, (hx_ly << 32));
+ result = this->add(result, (lx_hy << 32));
+ fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0;
+
+ return result;
+ }
+ }
+ bool fOK = true;
+};
+
+#endif // SkSafeMath_DEFINED
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d0124f5d4bad..7a1276982d0a 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -39,21 +39,22 @@
#include <SkShader.h>
#include <SkTextBlob.h>
#include <SkVertices.h>
+#include <log/log.h>
+#include <ui/FatVector.h>
#include <memory>
#include <optional>
#include <utility>
#include "CanvasProperty.h"
+#include "Mesh.h"
#include "NinePatchUtils.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
#include "hwui/PaintFilter.h"
-#include <log/log.h>
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/HolePunch.h"
-#include <ui/FatVector.h>
namespace android {
@@ -572,8 +573,14 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); });
}
-void SkiaCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
- mCanvas->drawMesh(mesh, blender, paint);
+void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) {
+ GrDirectContext* context = nullptr;
+ auto recordingContext = mCanvas->recordingContext();
+ if (recordingContext) {
+ context = recordingContext->asDirectContext();
+ }
+ mesh.updateSkMesh(context);
+ mCanvas->drawMesh(mesh.getSkMesh(), blender, paint);
}
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index f2c286a4fb46..b785989f35cb 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -129,8 +129,7 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) override;
virtual void drawPath(const SkPath& path, const Paint& paint) override;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override;
- virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
- const SkPaint& paint) override;
+ virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override;
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 2a2019199bda..44ee31d34d23 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -16,25 +16,25 @@
#pragma once
+#include <SaveFlags.h>
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkMatrix.h>
+#include <androidfw/ResourceTypes.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
-#include <SaveFlags.h>
-#include <androidfw/ResourceTypes.h>
#include "Properties.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "utils/Macros.h"
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkMatrix.h>
-
class SkAnimatedImage;
enum class SkBlendMode;
class SkCanvasState;
class SkRRect;
class SkRuntimeShaderBuilder;
class SkVertices;
+class Mesh;
namespace minikin {
class Font;
@@ -227,7 +227,7 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) = 0;
virtual void drawPath(const SkPath& path, const Paint& paint) = 0;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0;
- virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint& paint) = 0;
+ virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender>, const Paint& paint) = 0;
// Bitmap-based
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0;
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 6b983c10bdca..24f9e82b5340 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -46,10 +46,16 @@ public:
static void setJavaVM(JavaVM* javaVM);
- /** returns a pointer to the JavaVM provided when we initialized the module */
+ /**
+ * returns a pointer to the JavaVM provided when we initialized the module
+ * DEPRECATED: Objects should know the JavaVM that created them
+ */
static JavaVM* getJavaVM() { return mJavaVM; }
- /** return a pointer to the JNIEnv for this thread */
+ /**
+ * return a pointer to the JNIEnv for this thread
+ * DEPRECATED: Objects should know the JavaVM that created them
+ */
static JNIEnv* getJNIEnv();
/** create a JNIEnv* for this thread or assert if one already exists */
@@ -337,13 +343,21 @@ public:
JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
virtual ~JGlobalRefHolder() {
- GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject);
+ env()->DeleteGlobalRef(mObject);
mObject = nullptr;
}
jobject object() { return mObject; }
JavaVM* vm() { return mVm; }
+ JNIEnv* env() {
+ JNIEnv* env;
+ if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm);
+ }
+ return env;
+ }
+
private:
JGlobalRefHolder(const JGlobalRefHolder&) = delete;
void operator=(const JGlobalRefHolder&) = delete;
diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp
index fc3d70b87f5a..c71e3085caf5 100644
--- a/libs/hwui/jni/Interpolator.cpp
+++ b/libs/hwui/jni/Interpolator.cpp
@@ -24,12 +24,8 @@ static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHan
AutoJavaFloatArray autoValues(env, valueArray);
AutoJavaFloatArray autoBlend(env, blendArray, 4);
-#ifdef SK_SCALAR_IS_FLOAT
SkScalar* scalars = autoValues.ptr();
SkScalar* blend = autoBlend.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
interp->setKeyFrame(index, msec, scalars, blend);
}
diff --git a/libs/hwui/jni/JvmErrorReporter.h b/libs/hwui/jni/JvmErrorReporter.h
index 5e10b9d93275..3a3587572a1a 100644
--- a/libs/hwui/jni/JvmErrorReporter.h
+++ b/libs/hwui/jni/JvmErrorReporter.h
@@ -30,7 +30,10 @@ public:
JvmErrorReporter(JNIEnv* env) { env->GetJavaVM(&mVm); }
virtual void onError(const std::string& message) override {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
+ JNIEnv* env;
+ if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm);
+ }
jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
}
diff --git a/libs/hwui/jni/Mesh.cpp b/libs/hwui/jni/Mesh.cpp
deleted file mode 100644
index b13d9bafb417..000000000000
--- a/libs/hwui/jni/Mesh.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-#include <Mesh.h>
-#include <SkMesh.h>
-
-#include "GraphicsJNI.h"
-#include "graphics_jni_helpers.h"
-
-namespace android {
-
-sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size,
- jboolean isDirect) {
- auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
- auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size);
- return vertexBuffer;
-}
-
-sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size,
- jboolean isDirect) {
- auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
- auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size);
- return indexBuffer;
-}
-
-static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
- jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top,
- jfloat right, jfloat bottom) {
- auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
- sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
- genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect);
- auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
- auto meshResult = SkMesh::Make(
- skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
- SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
-
- if (!meshResult.error.isEmpty()) {
- jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
- }
-
- auto meshPtr = std::make_unique<MeshWrapper>(
- MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
- return reinterpret_cast<jlong>(meshPtr.release());
-}
-
-static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
- jboolean isVertexDirect, jint vertexCount, jint vertexOffset,
- jobject indexBuffer, jboolean isIndexDirect, jint indexCount,
- jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) {
- auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
- sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
- genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect);
- sk_sp<SkMesh::IndexBuffer> skIndexBuffer =
- genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect);
- auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
-
- auto meshResult = SkMesh::MakeIndexed(
- skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
- skIndexBuffer, indexCount, indexOffset,
- SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
-
- if (!meshResult.error.isEmpty()) {
- jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
- }
- auto meshPtr = std::make_unique<MeshWrapper>(
- MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
- return reinterpret_cast<jlong>(meshPtr.release());
-}
-
-static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) {
- auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
- auto mesh = wrapper->mesh;
- if (indexed) {
- wrapper->mesh = SkMesh::MakeIndexed(sk_ref_sp(mesh.spec()), mesh.mode(),
- sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(),
- mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()),
- mesh.indexCount(), mesh.indexOffset(),
- wrapper->builder.fUniforms, mesh.bounds())
- .mesh;
- } else {
- wrapper->mesh = SkMesh::Make(sk_ref_sp(mesh.spec()), mesh.mode(),
- sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(),
- mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds())
- .mesh;
- }
-}
-
-static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
- va_end(args);
- return ret;
-}
-
-static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
- switch (type) {
- case SkRuntimeEffect::Uniform::Type::kFloat:
- case SkRuntimeEffect::Uniform::Type::kFloat2:
- case SkRuntimeEffect::Uniform::Type::kFloat3:
- case SkRuntimeEffect::Uniform::Type::kFloat4:
- case SkRuntimeEffect::Uniform::Type::kFloat2x2:
- case SkRuntimeEffect::Uniform::Type::kFloat3x3:
- case SkRuntimeEffect::Uniform::Type::kFloat4x4:
- return false;
- case SkRuntimeEffect::Uniform::Type::kInt:
- case SkRuntimeEffect::Uniform::Type::kInt2:
- case SkRuntimeEffect::Uniform::Type::kInt3:
- case SkRuntimeEffect::Uniform::Type::kInt4:
- return true;
- }
-}
-
-static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
- const char* uniformName, const float values[], int count,
- bool isColor) {
- MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
- if (isColor) {
- jniThrowExceptionFmt(
- env, "java/lang/IllegalArgumentException",
- "attempting to set a color uniform using the non-color specific APIs: %s %x",
- uniformName, uniform.fVar->flags);
- } else {
- ThrowIAEFmt(env,
- "attempting to set a non-color uniform using the setColorUniform APIs: %s",
- uniformName);
- }
- } else if (isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<float>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
-static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
- jfloat value1, jfloat value2, jfloat value3, jfloat value4,
- jint count) {
- auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
- ScopedUtfChars name(env, uniformName);
- const float values[4] = {value1, value2, value3, value4};
- nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false);
-}
-
-static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
- jfloatArray jvalues, jboolean isColor) {
- auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
- ScopedUtfChars name(env, jUniformName);
- AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
- nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
- autoValues.length(), isColor);
-}
-
-static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
- const char* uniformName, const int values[], int count) {
- MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (!isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<int>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
-static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
- jint value1, jint value2, jint value3, jint value4, jint count) {
- auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
- ScopedUtfChars name(env, uniformName);
- const int values[4] = {value1, value2, value3, value4};
- nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count);
-}
-
-static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
- jintArray values) {
- auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
- ScopedUtfChars name(env, uniformName);
- AutoJavaIntArray autoValues(env, values, 0);
- nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
- autoValues.length());
-}
-
-static void MeshWrapper_destroy(MeshWrapper* wrapper) {
- delete wrapper;
-}
-
-static jlong getMeshFinalizer(JNIEnv*, jobject) {
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
-}
-
-static const JNINativeMethod gMeshMethods[] = {
- {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
- {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make},
- {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J",
- (void*)makeIndexed},
- {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh},
- {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
- {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
- {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
- {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
-
-int register_android_graphics_Mesh(JNIEnv* env) {
- android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
- return 0;
-}
-
-} // namespace android
diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h
deleted file mode 100644
index 61c2260e3ad1..000000000000
--- a/libs/hwui/jni/Mesh.h
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
-#define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
-
-#include <SkMesh.h>
-#include <jni.h>
-
-#include <log/log.h>
-#include <utility>
-
-#include "graphics_jni_helpers.h"
-
-#define gIndexByteSize 2
-
-// A smart pointer that provides read only access to Java.nio.Buffer. This handles both
-// direct and indrect buffers, allowing access to the underlying data in both
-// situations. If passed a null buffer, we will throw NullPointerException,
-// and c_data will return nullptr.
-//
-// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void *
-// conversion.
-class ScopedJavaNioBuffer {
-public:
- ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, jint size, jboolean isDirect)
- : mEnv(env), mBuffer(buffer) {
- if (buffer == nullptr) {
- mDataBase = nullptr;
- mData = nullptr;
- jniThrowNullPointerException(env);
- } else {
- mArray = (jarray) nullptr;
- if (isDirect) {
- mData = getDirectBufferPointer(mEnv, mBuffer);
- } else {
- mData = setIndirectData(size);
- }
- }
- }
-
- ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); }
-
- ~ScopedJavaNioBuffer() { reset(); }
-
- void reset() {
- if (mDataBase) {
- releasePointer(mEnv, mArray, mDataBase, JNI_FALSE);
- mDataBase = nullptr;
- }
- }
-
- ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept {
- if (this != &rhs) {
- reset();
-
- mEnv = rhs.mEnv;
- mBuffer = rhs.mBuffer;
- mDataBase = rhs.mDataBase;
- mData = rhs.mData;
- mArray = rhs.mArray;
- rhs.mEnv = nullptr;
- rhs.mData = nullptr;
- rhs.mBuffer = nullptr;
- rhs.mArray = nullptr;
- rhs.mDataBase = nullptr;
- }
- return *this;
- }
-
- const void* data() const { return mData; }
-
-private:
- /**
- * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data
- * from a java.nio.Buffer.
- */
- void* getDirectBufferPointer(JNIEnv* env, jobject buffer) {
- if (buffer == nullptr) {
- return nullptr;
- }
-
- jint position;
- jint limit;
- jint elementSizeShift;
- jlong pointer;
- pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
- if (pointer == 0) {
- jniThrowException(mEnv, "java/lang/IllegalArgumentException",
- "Must use a native order direct Buffer");
- return nullptr;
- }
- pointer += position << elementSizeShift;
- return reinterpret_cast<void*>(pointer);
- }
-
- static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) {
- env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
- }
-
- static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining,
- jint* offset) {
- jint position;
- jint limit;
- jint elementSizeShift;
-
- jlong pointer;
- pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
- *remaining = (limit - position) << elementSizeShift;
- if (pointer != 0L) {
- *array = nullptr;
- pointer += position << elementSizeShift;
- return reinterpret_cast<void*>(pointer);
- }
-
- *array = jniGetNioBufferBaseArray(env, buffer);
- *offset = jniGetNioBufferBaseArrayOffset(env, buffer);
- return nullptr;
- }
-
- /**
- * This is a copy of
- * static void android_glBufferData__IILjava_nio_Buffer_2I
- * from com_google_android_gles_jni_GLImpl.cpp
- */
- void* setIndirectData(jint size) {
- jint exception;
- const char* exceptionType;
- const char* exceptionMessage;
- jint bufferOffset = (jint)0;
- jint remaining;
- void* tempData;
-
- if (mBuffer) {
- tempData =
- (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset);
- if (remaining < size) {
- exception = 1;
- exceptionType = "java/lang/IllegalArgumentException";
- exceptionMessage = "remaining() < size < needed";
- goto exit;
- }
- }
- if (mBuffer && tempData == nullptr) {
- mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0);
- tempData = (void*)(mDataBase + bufferOffset);
- }
- return tempData;
- exit:
- if (mArray) {
- releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE);
- }
- if (exception) {
- jniThrowException(mEnv, exceptionType, exceptionMessage);
- }
- return nullptr;
- }
-
- JNIEnv* mEnv;
-
- // Java Buffer data
- void* mData;
- jobject mBuffer;
-
- // Indirect Buffer Data
- jarray mArray;
- char* mDataBase;
-};
-
-class MeshUniformBuilder {
-public:
- struct MeshUniform {
- template <typename T>
- std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
- const T& val) {
- if (!fVar) {
- LOG_FATAL("Assigning to missing variable");
- } else if (sizeof(val) != fVar->sizeInBytes()) {
- LOG_FATAL("Incorrect value size");
- } else {
- void* dst = reinterpret_cast<void*>(
- reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
- memcpy(dst, &val, sizeof(val));
- }
- }
-
- MeshUniform& operator=(const SkMatrix& val) {
- if (!fVar) {
- LOG_FATAL("Assigning to missing variable");
- } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
- LOG_FATAL("Incorrect value size");
- } else {
- float* data = reinterpret_cast<float*>(
- reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
- data[0] = val.get(0);
- data[1] = val.get(3);
- data[2] = val.get(6);
- data[3] = val.get(1);
- data[4] = val.get(4);
- data[5] = val.get(7);
- data[6] = val.get(2);
- data[7] = val.get(5);
- data[8] = val.get(8);
- }
- return *this;
- }
-
- template <typename T>
- bool set(const T val[], const int count) {
- static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
- if (!fVar) {
- LOG_FATAL("Assigning to missing variable");
- return false;
- } else if (sizeof(T) * count != fVar->sizeInBytes()) {
- LOG_FATAL("Incorrect value size");
- return false;
- } else {
- void* dst = reinterpret_cast<void*>(
- reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
- memcpy(dst, val, sizeof(T) * count);
- }
- return true;
- }
-
- MeshUniformBuilder* fOwner;
- const SkRuntimeEffect::Uniform* fVar;
- };
- MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; }
-
- explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) {
- fMeshSpec = sk_sp(meshSpec);
- fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
- }
-
- sk_sp<SkData> fUniforms;
-
-private:
- void* writableUniformData() {
- if (!fUniforms->unique()) {
- fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
- }
- return fUniforms->writable_data();
- }
-
- sk_sp<SkMeshSpecification> fMeshSpec;
-};
-
-struct MeshWrapper {
- SkMesh mesh;
- MeshUniformBuilder builder;
-};
-#endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp
index 3694ce07b972..a5e04763d885 100644
--- a/libs/hwui/jni/Path.cpp
+++ b/libs/hwui/jni/Path.cpp
@@ -182,11 +182,7 @@ public:
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
AutoJavaFloatArray afa(env, array, 8);
-#ifdef SK_SCALAR_IS_FLOAT
const float* src = afa.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
obj->addRoundRect(rect, src, dir);
}
diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp
index f99bef7b7d58..3dbe1a67f52e 100644
--- a/libs/hwui/jni/PathEffect.cpp
+++ b/libs/hwui/jni/PathEffect.cpp
@@ -35,11 +35,7 @@ public:
jfloatArray intervalArray, jfloat phase) {
AutoJavaFloatArray autoInterval(env, intervalArray);
int count = autoInterval.length() & ~1; // even number
-#ifdef SK_SCALAR_IS_FLOAT
- SkScalar* intervals = autoInterval.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
+ SkScalar* intervals = autoInterval.ptr();
SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release();
return reinterpret_cast<jlong>(effect);
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 8a0db1c91d46..75d45e5bd8aa 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -52,12 +52,7 @@ static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue
static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
{
AutoJavaFloatArray autoHSV(env, hsvArray, 3);
-#ifdef SK_SCALAR_IS_FLOAT
- SkScalar* hsv = autoHSV.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
-
+ SkScalar* hsv = autoHSV.ptr();
return static_cast<jint>(SkHSVToColor(alpha, hsv));
}
@@ -149,11 +144,7 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
AutoJavaFloatArray autoPos(env, posArray, colors.size());
-#ifdef SK_SCALAR_IS_FLOAT
SkScalar* pos = autoPos.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
@@ -193,11 +184,7 @@ static jlong RadialGradient_create(JNIEnv* env,
std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
AutoJavaFloatArray autoPos(env, posArray, colors.size());
-#ifdef SK_SCALAR_IS_FLOAT
SkScalar* pos = autoPos.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
auto skTileMode = static_cast<SkTileMode>(tileMode);
@@ -225,11 +212,7 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat
std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
AutoJavaFloatArray autoPos(env, jpositions, colors.size());
-#ifdef SK_SCALAR_IS_FLOAT
SkScalar* pos = autoPos.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 8a4d4e17edb1..8ba750372d18 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -21,7 +21,6 @@
#else
#define __ANDROID_API_P__ 28
#endif
-#include <Mesh.h>
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
@@ -446,10 +445,10 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle,
jlong paintHandle) {
- const SkMesh mesh = reinterpret_cast<MeshWrapper*>(meshHandle)->mesh;
+ const Mesh* mesh = reinterpret_cast<Mesh*>(meshHandle);
SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle);
- SkPaint* paint = reinterpret_cast<Paint*>(paintHandle);
- get_canvas(canvasHandle)->drawMesh(mesh, SkBlender::Mode(blendMode), *paint);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawMesh(*mesh, SkBlender::Mode(blendMode), *paint);
}
static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
index 3e453e65ae92..ae22213f4bf4 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -49,7 +49,7 @@ static RenderCallback createRenderCallback(JNIEnv* env, jobject releaseCallback)
auto globalCallbackRef =
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(releaseCallback));
return [globalCallbackRef](android::base::unique_fd&& fd, int status) {
- GraphicsJNI::getJNIEnv()->CallStaticVoidMethod(
+ globalCallbackRef->env()->CallStaticVoidMethod(
gHardwareBufferRendererClassInfo.clazz,
gHardwareBufferRendererClassInfo.invokeRenderCallback, globalCallbackRef->object(),
reinterpret_cast<jint>(fd.release()), reinterpret_cast<jint>(status));
@@ -172,7 +172,8 @@ static const JNINativeMethod gMethods[] = {
int register_android_graphics_HardwareBufferRenderer(JNIEnv* env) {
jclass hardwareBufferRendererClazz =
FindClassOrDie(env, "android/graphics/HardwareBufferRenderer");
- gHardwareBufferRendererClassInfo.clazz = hardwareBufferRendererClazz;
+ gHardwareBufferRendererClassInfo.clazz =
+ reinterpret_cast<jclass>(env->NewGlobalRef(hardwareBufferRendererClazz));
gHardwareBufferRendererClassInfo.invokeRenderCallback =
GetStaticMethodIDOrDie(env, hardwareBufferRendererClazz, "invokeRenderCallback",
"(Ljava/util/function/Consumer;II)V");
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index d6aad7d3eede..6a7411f5d859 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -94,12 +94,21 @@ struct {
jmethodID getDestinationBitmap;
} gCopyRequest;
+static JNIEnv* getenv(JavaVM* vm) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+ }
+ return env;
+}
+
typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
ANW_fromSurface fromSurface;
class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
public:
explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
+ env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
@@ -109,18 +118,19 @@ public:
void onFrameCommit(bool didProduceBuffer) {
if (mObject) {
ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
- GraphicsJNI::getJNIEnv()->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
- didProduceBuffer);
+ getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
+ didProduceBuffer);
releaseObject();
}
}
private:
+ JavaVM* mVm;
jobject mObject;
void releaseObject() {
if (mObject) {
- GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject);
+ getenv(mVm)->DeleteGlobalRef(mObject);
mObject = nullptr;
}
}
@@ -541,10 +551,9 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv*
auto pictureState = std::make_shared<PictureCaptureState>();
proxy->setPictureCapturedCallback([globalCallbackRef,
pictureState](sk_sp<SkPicture>&& picture) {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
Picture* wrapper = new PictureWrapper{std::move(picture), pictureState};
- env->CallStaticVoidMethod(gHardwareRenderer.clazz,
- gHardwareRenderer.invokePictureCapturedCallback,
+ globalCallbackRef->env()->CallStaticVoidMethod(
+ gHardwareRenderer.clazz, gHardwareRenderer.invokePictureCapturedCallback,
static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)),
globalCallbackRef->object());
});
@@ -561,16 +570,14 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
vm, env->NewGlobalRef(aSurfaceTransactionCallback));
- proxy->setASurfaceTransactionCallback(
- [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
- jboolean ret = env->CallBooleanMethod(
- globalCallbackRef->object(),
- gASurfaceTransactionCallback.onMergeTransaction,
- static_cast<jlong>(transObj), static_cast<jlong>(scObj),
- static_cast<jlong>(frameNr));
- return ret;
- });
+ proxy->setASurfaceTransactionCallback([globalCallbackRef](int64_t transObj, int64_t scObj,
+ int64_t frameNr) -> bool {
+ jboolean ret = globalCallbackRef->env()->CallBooleanMethod(
+ globalCallbackRef->object(), gASurfaceTransactionCallback.onMergeTransaction,
+ static_cast<jlong>(transObj), static_cast<jlong>(scObj),
+ static_cast<jlong>(frameNr));
+ return ret;
+ });
}
}
@@ -585,9 +592,8 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall
auto globalCallbackRef =
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
- env->CallVoidMethod(globalCallbackRef->object(),
- gPrepareSurfaceControlForWebviewCallback.prepare);
+ globalCallbackRef->env()->CallVoidMethod(
+ globalCallbackRef->object(), gPrepareSurfaceControlForWebviewCallback.prepare);
});
}
}
@@ -604,7 +610,7 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
env->NewGlobalRef(frameCallback));
proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
int64_t frameNr) -> std::function<void(bool)> {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
+ JNIEnv* env = globalCallbackRef->env();
ScopedLocalRef<jobject> frameCommitCallback(
env, env->CallObjectMethod(
globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
@@ -643,9 +649,8 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
auto globalCallbackRef =
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setFrameCompleteCallback([globalCallbackRef]() {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
- env->CallVoidMethod(globalCallbackRef->object(),
- gFrameCompleteCallback.onFrameComplete);
+ globalCallbackRef->env()->CallVoidMethod(globalCallbackRef->object(),
+ gFrameCompleteCallback.onFrameComplete);
});
}
}
@@ -656,8 +661,7 @@ public:
: CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
- jlong bitmapPtr = env->CallLongMethod(
+ jlong bitmapPtr = mRefHolder.env()->CallLongMethod(
mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
SkBitmap bitmap;
bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
@@ -665,9 +669,8 @@ public:
}
virtual void onCopyFinished(CopyResult result) override {
- JNIEnv* env = GraphicsJNI::getJNIEnv();
- env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
- static_cast<jint>(result));
+ mRefHolder.env()->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+ static_cast<jint>(result));
}
private:
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index cf6702e45fff..ca667b0d09bc 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -23,8 +23,6 @@ namespace android {
static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), "
"update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here");
-static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, "
- "only float scalar is supported");
class SkMatrixGlue {
public:
diff --git a/libs/hwui/jni/android_graphics_Mesh.cpp b/libs/hwui/jni/android_graphics_Mesh.cpp
new file mode 100644
index 000000000000..04339dc8b9a5
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Mesh.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GrDirectContext.h>
+#include <Mesh.h>
+#include <SkMesh.h>
+#include <jni.h>
+#include <log/log.h>
+
+#include <utility>
+
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+#define gIndexByteSize 2
+
+// A smart pointer that provides read only access to Java.nio.Buffer. This handles both
+// direct and indrect buffers, allowing access to the underlying data in both
+// situations. If passed a null buffer, we will throw NullPointerException,
+// and c_data will return nullptr.
+//
+// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void *
+// conversion.
+class ScopedJavaNioBuffer {
+public:
+ ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, size_t size, jboolean isDirect)
+ : mEnv(env), mBuffer(buffer) {
+ if (buffer == nullptr) {
+ mDataBase = nullptr;
+ mData = nullptr;
+ jniThrowNullPointerException(env);
+ } else {
+ mArray = (jarray) nullptr;
+ if (isDirect) {
+ mData = getDirectBufferPointer(mEnv, mBuffer);
+ } else {
+ mData = setIndirectData(size);
+ }
+ }
+ }
+
+ ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); }
+
+ ~ScopedJavaNioBuffer() { reset(); }
+
+ void reset() {
+ if (mDataBase) {
+ releasePointer(mEnv, mArray, mDataBase, JNI_FALSE);
+ mDataBase = nullptr;
+ }
+ }
+
+ ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept {
+ if (this != &rhs) {
+ reset();
+
+ mEnv = rhs.mEnv;
+ mBuffer = rhs.mBuffer;
+ mDataBase = rhs.mDataBase;
+ mData = rhs.mData;
+ mArray = rhs.mArray;
+ rhs.mEnv = nullptr;
+ rhs.mData = nullptr;
+ rhs.mBuffer = nullptr;
+ rhs.mArray = nullptr;
+ rhs.mDataBase = nullptr;
+ }
+ return *this;
+ }
+
+ const void* data() const { return mData; }
+
+private:
+ /**
+ * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data
+ * from a java.nio.Buffer.
+ */
+ void* getDirectBufferPointer(JNIEnv* env, jobject buffer) {
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+ pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
+ if (pointer == 0) {
+ jniThrowException(mEnv, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
+ return nullptr;
+ }
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) {
+ env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
+ }
+
+ static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining,
+ jint* offset) {
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+
+ jlong pointer;
+ pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
+ *remaining = (limit - position) << elementSizeShift;
+ if (pointer != 0L) {
+ *array = nullptr;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ *array = jniGetNioBufferBaseArray(env, buffer);
+ *offset = jniGetNioBufferBaseArrayOffset(env, buffer);
+ return nullptr;
+ }
+
+ /**
+ * This is a copy of
+ * static void android_glBufferData__IILjava_nio_Buffer_2I
+ * from com_google_android_gles_jni_GLImpl.cpp
+ */
+ void* setIndirectData(size_t size) {
+ jint exception;
+ const char* exceptionType;
+ const char* exceptionMessage;
+ jint bufferOffset = (jint)0;
+ jint remaining;
+ void* tempData;
+
+ if (mBuffer) {
+ tempData =
+ (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset);
+ if (remaining < size) {
+ exception = 1;
+ exceptionType = "java/lang/IllegalArgumentException";
+ exceptionMessage = "remaining() < size < needed";
+ goto exit;
+ }
+ }
+ if (mBuffer && tempData == nullptr) {
+ mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0);
+ tempData = (void*)(mDataBase + bufferOffset);
+ }
+ return tempData;
+ exit:
+ if (mArray) {
+ releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE);
+ }
+ if (exception) {
+ jniThrowException(mEnv, exceptionType, exceptionMessage);
+ }
+ return nullptr;
+ }
+
+ JNIEnv* mEnv;
+
+ // Java Buffer data
+ void* mData;
+ jobject mBuffer;
+
+ // Indirect Buffer Data
+ jarray mArray;
+ char* mDataBase;
+};
+
+namespace android {
+
+static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
+ jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top,
+ jfloat right, jfloat bottom) {
+ auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
+ size_t bufferSize = vertexCount * skMeshSpec->stride();
+ auto buff = ScopedJavaNioBuffer(env, vertexBuffer, bufferSize, isDirect);
+ auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
+ auto meshPtr = new Mesh(skMeshSpec, mode, buff.data(), bufferSize, vertexCount, vertexOffset,
+ std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect);
+ auto [valid, msg] = meshPtr->validate();
+ if (!valid) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
+ }
+ return reinterpret_cast<jlong>(meshPtr);
+}
+
+static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
+ jboolean isVertexDirect, jint vertexCount, jint vertexOffset,
+ jobject indexBuffer, jboolean isIndexDirect, jint indexCount,
+ jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) {
+ auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
+ auto vertexBufferSize = vertexCount * skMeshSpec->stride();
+ auto indexBufferSize = indexCount * gIndexByteSize;
+ auto vBuf = ScopedJavaNioBuffer(env, vertexBuffer, vertexBufferSize, isVertexDirect);
+ auto iBuf = ScopedJavaNioBuffer(env, indexBuffer, indexBufferSize, isIndexDirect);
+ auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
+ auto meshPtr = new Mesh(skMeshSpec, mode, vBuf.data(), vertexBufferSize, vertexCount,
+ vertexOffset, iBuf.data(), indexBufferSize, indexCount, indexOffset,
+ std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect);
+ auto [valid, msg] = meshPtr->validate();
+ if (!valid) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
+ }
+
+ return reinterpret_cast<jlong>(meshPtr);
+}
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
+ const char* uniformName, const float values[], int count,
+ bool isColor) {
+ MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jfloat value1, jfloat value2, jfloat value3, jfloat value4,
+ jint count) {
+ auto* wrapper = reinterpret_cast<Mesh*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count, false);
+ wrapper->markDirty();
+}
+
+static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
+ jfloatArray jvalues, jboolean isColor) {
+ auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+ nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
+ autoValues.length(), isColor);
+ wrapper->markDirty();
+}
+
+static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
+ const char* uniformName, const int values[], int count) {
+ MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jint value1, jint value2, jint value3, jint value4, jint count) {
+ auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count);
+ wrapper->markDirty();
+}
+
+static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jintArray values) {
+ auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaIntArray autoValues(env, values, 0);
+ nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
+ autoValues.length());
+ wrapper->markDirty();
+}
+
+static void MeshWrapper_destroy(Mesh* wrapper) {
+ delete wrapper;
+}
+
+static jlong getMeshFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
+}
+
+static const JNINativeMethod gMeshMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
+ {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make},
+ {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J",
+ (void*)makeIndexed},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
+
+int register_android_graphics_Mesh(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
+ return 0;
+}
+
+} // namespace android \ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index fcfc4f82abed..af2d3b34bac7 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -23,6 +23,7 @@
#else
#include "DamageAccumulator.h"
#endif
+#include "TreeInfo.h"
#include "VectorDrawable.h"
#ifdef __ANDROID__
#include "renderthread/CanvasContext.h"
@@ -102,6 +103,12 @@ bool SkiaDisplayList::prepareListAndChildren(
info.prepareTextures = false;
info.canvasContext.unpinImages();
}
+
+ auto grContext = info.canvasContext.getGrContext();
+ for (auto mesh : mMeshes) {
+ mesh->updateSkMesh(grContext);
+ }
+
#endif
bool hasBackwardProjectedNodesHere = false;
@@ -168,6 +175,7 @@ void SkiaDisplayList::reset() {
mDisplayList.reset();
+ mMeshes.clear();
mMutableImages.clear();
mVectorDrawables.clear();
mAnimatedImages.clear();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 2a677344b7b2..7af31a4dc4c6 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -18,6 +18,7 @@
#include <deque>
+#include "Mesh.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
@@ -167,6 +168,7 @@ public:
std::deque<RenderNodeDrawable> mChildNodes;
std::deque<FunctorDrawable*> mChildFunctors;
std::vector<SkImage*> mMutableImages;
+ std::vector<const Mesh*> mMeshes;
private:
std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index e1c8877a8b7a..3ca7eeb37a89 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -321,6 +321,11 @@ double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedIma
return 0;
}
+void SkiaRecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) {
+ mDisplayList->mMeshes.push_back(&mesh);
+ mRecorder.drawMesh(mesh, blender, paint);
+}
+
} // namespace skiapipeline
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 3fd8fa3aa9a9..a8e4580dc200 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -81,6 +81,7 @@ public:
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void enableZ(bool enableZ) override;
+ virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5136675a95eb..4da4c7fc56de 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,9 +16,9 @@
package android.media;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import android.Manifest;
import android.annotation.CallbackExecutor;
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index b6ab262b60c5..7faa13c80c62 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,9 +16,9 @@
package android.media;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import android.annotation.CallbackExecutor;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 0b086816ca4b..faa7f7fa3aa1 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -3049,7 +3049,13 @@ public final class MediaDrm implements AutoCloseable {
/**
- * Gets the {@link LogSessionId}.
+ * Sets the {@link LogSessionId}.
+ *
+ * <p>The implementation of this method varies by DRM provider; Please refer
+ * to your DRM provider documentation for more details on this method.
+ *
+ * @throws UnsupportedOperationException when the vendor plugin does not
+ * implement this method
*/
public void setLogSessionId(@NonNull LogSessionId logSessionId) {
Objects.requireNonNull(logSessionId);
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 38115e19d608..3f44b09124e4 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -16,9 +16,9 @@
package android.media;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import android.annotation.NonNull;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 76543f4ce989..6089f4291f3e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -17,10 +17,10 @@
package com.android.mediaframeworktest.unit;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.FX_KEY_CLICK;
import static org.mockito.ArgumentMatchers.anyInt;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioRecordUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioRecordUnitTest.java
index 9c813c28d7d9..71228e2120c0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioRecordUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioRecordUnitTest.java
@@ -16,10 +16,10 @@
package com.android.mediaframeworktest.unit;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import static org.junit.Assert.assertEquals;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioTrackUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioTrackUnitTest.java
index ffed39ac82bf..ac24a1007170 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioTrackUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioTrackUnitTest.java
@@ -16,10 +16,10 @@
package com.android.mediaframeworktest.unit;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import static org.junit.Assert.assertEquals;
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index b36cb5c9709f..dfc8aa06c404 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -41,6 +41,15 @@
android:excludeFromRecents="true"
android:theme="@style/Theme.CredentialSelector">
</activity>
+
+ <receiver
+ android:name=".CredentialProviderReceiver"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR">
+ <intent-filter>
+ <action android:name="android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED"/>
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 3eb58f1532d8..ee512427fa9c 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -94,6 +94,8 @@
<string name="accessibility_back_arrow_button">"Go back to the previous page"</string>
<!-- Spoken content description of the close "X" icon button. -->
<string name="accessibility_close_button">Close</string>
+ <!-- Spoken content description of the close "X" icon button. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_snackbar_dismiss">Dismiss</string>
<!-- Strings for the get flow. -->
<!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/res/values/themes.xml b/packages/CredentialManager/res/values/themes.xml
index a58a0383b9b2..c7e47962472f 100644
--- a/packages/CredentialManager/res/values/themes.xml
+++ b/packages/CredentialManager/res/values/themes.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
-
<style name="Theme.CredentialSelector" parent="@android:style/ThemeOverlay.Material">
- <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="fontFamily">google-sans</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index e3236b38b0f7..0e604ce2cf3a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -16,8 +16,6 @@
package com.android.credentialmanager
-import android.app.slice.Slice
-import android.app.slice.SliceSpec
import android.content.Context
import android.content.Intent
import android.credentials.CreateCredentialRequest
@@ -25,6 +23,7 @@ import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.credentials.CredentialOption
import android.credentials.GetCredentialRequest
import android.credentials.ui.AuthenticationEntry
+import android.credentials.ui.CancelUiRequest
import android.credentials.ui.Constants
import android.credentials.ui.Entry
import android.credentials.ui.CreateCredentialProviderData
@@ -35,7 +34,6 @@ import android.credentials.ui.RequestInfo
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.ProviderPendingIntentResponse
import android.credentials.ui.UserSelectionDialogResult
-import android.net.Uri
import android.os.IBinder
import android.os.Binder
import android.os.Bundle
@@ -47,6 +45,8 @@ import com.android.credentialmanager.getflow.GetCredentialUiState
import androidx.credentials.CreateCredentialRequest.DisplayInfo
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.GetPasswordOption
+import androidx.credentials.GetPublicKeyCredentialOption
import java.time.Instant
@@ -75,6 +75,12 @@ class CredentialManagerRepo(
RequestInfo::class.java
) ?: testCreatePasskeyRequestInfo()
+ val originName: String? = when (requestInfo.type) {
+ RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin
+ RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin
+ else -> null
+ }
+
providerEnabledList = when (requestInfo.type) {
RequestInfo.TYPE_CREATE ->
intent.extras?.getParcelableArrayList(
@@ -108,15 +114,15 @@ class CredentialManagerRepo(
val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
val providerEnableListUiState = getCreateProviderEnableListInitialUiState()
val providerDisableListUiState = getCreateProviderDisableListInitialUiState()
- val requestDisplayInfoUiState = getCreateRequestDisplayInfoInitialUiState()!!
+ val requestDisplayInfoUiState =
+ getCreateRequestDisplayInfoInitialUiState(originName)!!
UiState(
createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState(
providerEnableListUiState,
providerDisableListUiState,
defaultProviderId,
requestDisplayInfoUiState,
- /** isOnPasskeyIntroStateAlready = */
- false,
+ isOnPasskeyIntroStateAlready = false,
isPasskeyFirstUse
)!!,
getCredentialUiState = null,
@@ -124,7 +130,7 @@ class CredentialManagerRepo(
}
RequestInfo.TYPE_GET -> UiState(
createCredentialUiState = null,
- getCredentialUiState = getCredentialInitialUiState()!!,
+ getCredentialUiState = getCredentialInitialUiState(originName)!!,
)
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
}
@@ -175,11 +181,11 @@ class CredentialManagerRepo(
}
// IMPORTANT: new invocation should be mindful that this method can throw.
- private fun getCredentialInitialUiState(): GetCredentialUiState? {
+ private fun getCredentialInitialUiState(originName: String?): GetCredentialUiState? {
val providerEnabledList = GetFlowUtils.toProviderList(
providerEnabledList as List<GetCredentialProviderData>, context
)
- val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context)
+ val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context, originName)
return GetCredentialUiState(
providerEnabledList,
requestDisplayInfo ?: return null,
@@ -201,8 +207,10 @@ class CredentialManagerRepo(
)
}
- private fun getCreateRequestDisplayInfoInitialUiState(): RequestDisplayInfo? {
- return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context)
+ private fun getCreateRequestDisplayInfoInitialUiState(
+ originName: String?
+ ): RequestDisplayInfo? {
+ return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context, originName)
}
companion object {
@@ -217,6 +225,14 @@ class CredentialManagerRepo(
resultReceiver.send(cancelCode, resultData)
}
}
+
+ /** Return the request token whose UI should be cancelled, or null otherwise. */
+ fun getCancelUiRequestToken(intent: Intent): IBinder? {
+ return intent.extras?.getParcelable(
+ CancelUiRequest.EXTRA_CANCEL_UI_REQUEST,
+ CancelUiRequest::class.java
+ )?.token
+ }
}
// TODO: below are prototype functionalities. To be removed for productionization.
@@ -230,7 +246,8 @@ class CredentialManagerRepo(
context,
"key1", "subkey-1", "elisa.beckett@gmail.com",
20, 7, 27, Instant.ofEpochSecond(10L),
- "Legal note"
+ "You can use your passkey on this or other devices. It is saved to " +
+ "the Password Manager for elisa.beckett@gmail.com."
),
CreateTestUtils.newCreateEntry(
context,
@@ -239,11 +256,9 @@ class CredentialManagerRepo(
null
),
)
- )
- .setRemoteEntry(
- newRemoteEntry("key2", "subkey-1")
- )
- .build(),
+ ).setRemoteEntry(
+ CreateTestUtils.newRemoteCreateEntry(context, "key2", "subkey-1")
+ ).build(),
CreateCredentialProviderData
.Builder("com.dashlane")
.setSaveEntries(
@@ -258,11 +273,11 @@ class CredentialManagerRepo(
context,
"key1", "subkey-4", "elisa.work@dashlane.com",
20, 7, 27, Instant.ofEpochSecond(14L),
- null
+ "You can use your passkey on this or other devices. It is saved to " +
+ "the Password Manager for elisa.work@dashlane.com"
),
)
- )
- .build(),
+ ).build(),
)
}
@@ -318,7 +333,7 @@ class CredentialManagerRepo(
),
)
).setRemoteEntry(
- newRemoteEntry("key4", "subkey-1")
+ GetTestUtils.newRemoteCredentialEntry(context, "key4", "subkey-1")
).build(),
GetCredentialProviderData.Builder("com.dashlane")
.setCredentialEntries(
@@ -333,10 +348,12 @@ class CredentialManagerRepo(
),
)
).setAuthenticationEntries(
- listOf(GetTestUtils.newAuthenticationEntry(
- context, "key2", "subkey-1", "foo@email.com",
- AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT
- ))
+ listOf(
+ GetTestUtils.newAuthenticationEntry(
+ context, "key2", "subkey-1", "foo@email.com",
+ AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT,
+ )
+ )
).setActionChips(
listOf(
GetTestUtils.newActionEntry(
@@ -348,20 +365,6 @@ class CredentialManagerRepo(
)
}
-
- private fun newRemoteEntry(
- key: String,
- subkey: String,
- ): Entry {
- return Entry(
- key,
- subkey,
- Slice.Builder(
- Uri.EMPTY, SliceSpec("type", 1)
- ).build()
- )
- }
-
private fun testCreatePasskeyRequestInfo(): RequestInfo {
val request = CreatePublicKeyCredentialRequest(
"{\"extensions\": {\n" +
@@ -398,7 +401,8 @@ class CredentialManagerRepo(
" \"authenticatorSelection\": {\n" +
" \"residentKey\": \"required\",\n" +
" \"requireResidentKey\": true\n" +
- " }}"
+ " }}",
+ preferImmediatelyAvailableCredentials = true,
)
val credentialData = request.credentialData
return RequestInfo.newCreateRequestInfo(
@@ -444,19 +448,28 @@ class CredentialManagerRepo(
}
private fun testGetRequestInfo(): RequestInfo {
+ val passwordOption = GetPasswordOption()
+ val passkeyOption = GetPublicKeyCredentialOption(
+ "json", preferImmediatelyAvailableCredentials = false)
return RequestInfo.newGetRequestInfo(
Binder(),
GetCredentialRequest.Builder(
Bundle()
).addCredentialOption(
CredentialOption(
- "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
- Bundle(),
- Bundle(), /*isSystemProviderRequired=*/
- false
+ passwordOption.type,
+ passwordOption.requestData,
+ passwordOption.candidateQueryData,
+ passwordOption.isSystemProviderRequired
)
- )
- .build(),
+ ).addCredentialOption(
+ CredentialOption(
+ passkeyOption.type,
+ passkeyOption.requestData,
+ passkeyOption.candidateQueryData,
+ passkeyOption.isSystemProviderRequired
+ )
+ ).build(),
"com.google.android.youtube"
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt
new file mode 100644
index 000000000000..ee8cffed7f7d
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.common.Constants
+
+
+class CredentialProviderReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context?, intent: Intent?) {
+ Log.d(Constants.LOG_TAG, "Received intent in CredentialProviderReceiver")
+
+ val sharedPreferences = context?.getSharedPreferences(context?.packageName,
+ Context.MODE_PRIVATE)
+ sharedPreferences?.edit()?.remove(UserConfigRepo.DEFAULT_PROVIDER)?.commit()
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index d618e746f4b8..e8e39741c3bd 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -39,7 +39,7 @@ import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.hasContentToDisplay
import com.android.credentialmanager.getflow.GetCredentialScreen
import com.android.credentialmanager.getflow.hasContentToDisplay
-import com.android.credentialmanager.ui.theme.CredentialSelectorTheme
+import com.android.credentialmanager.ui.theme.PlatformTheme
@ExperimentalMaterialApi
class CredentialSelectorActivity : ComponentActivity() {
@@ -47,10 +47,16 @@ class CredentialSelectorActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity")
try {
+ if (CredentialManagerRepo.getCancelUiRequestToken(intent) != null) {
+ Log.d(
+ Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.")
+ this.finish()
+ return
+ }
val userConfigRepo = UserConfigRepo(this)
val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo)
setContent {
- CredentialSelectorTheme {
+ PlatformTheme {
CredentialManagerBottomSheet(
credManRepo,
userConfigRepo
@@ -67,10 +73,19 @@ class CredentialSelectorActivity : ComponentActivity() {
setIntent(intent)
Log.d(Constants.LOG_TAG, "Existing activity received new intent")
try {
- val userConfigRepo = UserConfigRepo(this)
- val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo)
+ val cancelUiRequestToken = CredentialManagerRepo.getCancelUiRequestToken(intent)
val viewModel: CredentialSelectorViewModel by viewModels()
- viewModel.onNewCredentialManagerRepo(credManRepo)
+ if (cancelUiRequestToken != null &&
+ viewModel.shouldCancelCurrentUi(cancelUiRequestToken)) {
+ Log.d(
+ Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.")
+ this.finish()
+ return
+ } else {
+ val userConfigRepo = UserConfigRepo(this)
+ val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo)
+ viewModel.onNewCredentialManagerRepo(credManRepo)
+ }
} catch (e: Exception) {
onInitializationError(e, intent)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index a1e0823e4f96..9b7139ccc26e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.app.Activity
+import android.os.IBinder
import android.util.Log
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
@@ -135,6 +136,11 @@ class CredentialSelectorViewModel(
uiState = uiState.copy(dialogState = DialogState.COMPLETE)
}
+ /** Return true if the current UI's request token matches the UI cancellation request token. */
+ fun shouldCancelCurrentUi(cancelRequestToken: IBinder): Boolean {
+ return credManRepo.requestInfo.token.equals(cancelRequestToken)
+ }
+
/**************************************************************************/
/***** Get Flow Callbacks *****/
/**************************************************************************/
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index aa0959c82b4b..a834994a5b0f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -50,7 +50,11 @@ import com.android.credentialmanager.getflow.RemoteEntryInfo
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.CredentialOption
import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CreatePublicKeyCredentialRequestPrivileged
+import androidx.credentials.GetPublicKeyCredentialOption
+import androidx.credentials.GetPublicKeyCredentialOptionPrivileged
import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import androidx.credentials.provider.Action
import androidx.credentials.provider.AuthenticationAction
@@ -170,10 +174,29 @@ class GetFlowUtils {
fun toRequestDisplayInfo(
requestInfo: RequestInfo,
context: Context,
+ originName: String?,
): com.android.credentialmanager.getflow.RequestDisplayInfo? {
+ val getCredentialRequest = requestInfo.getCredentialRequest ?: return null
+ val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any {
+ val credentialOptionJetpack = CredentialOption.createFrom(
+ it.type,
+ it.credentialRetrievalData,
+ it.credentialRetrievalData,
+ it.isSystemProviderRequired
+ )
+ if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
+ credentialOptionJetpack.preferImmediatelyAvailableCredentials
+ } else if (credentialOptionJetpack is GetPublicKeyCredentialOptionPrivileged) {
+ credentialOptionJetpack.preferImmediatelyAvailableCredentials
+ } else {
+ false
+ }
+ }
return com.android.credentialmanager.getflow.RequestDisplayInfo(
- appName = getAppLabel(context.packageManager, requestInfo.appPackageName)
- ?: return null
+ appName = originName
+ ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
+ ?: return null,
+ preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
)
}
@@ -287,6 +310,7 @@ class GetFlowUtils {
pendingIntent = structuredAuthEntry.pendingIntent,
fillInIntent = entry.frameworkExtrasIntent,
title = title,
+ providerDisplayName = providerDisplayName,
icon = providerIcon,
isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
isLastUnlocked =
@@ -394,8 +418,10 @@ class CreateFlowUtils {
fun toRequestDisplayInfo(
requestInfo: RequestInfo,
context: Context,
+ originName: String?,
): RequestDisplayInfo? {
- val appLabel = getAppLabel(context.packageManager, requestInfo.appPackageName)
+ val appLabel = originName
+ ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
?: return null
val createCredentialRequest = requestInfo.createCredentialRequest ?: return null
val createCredentialRequestJetpack = CreateCredentialRequest.createFrom(
@@ -410,24 +436,25 @@ class CreateFlowUtils {
createCredentialRequestJetpack.password,
CredentialType.PASSWORD,
appLabel,
- context.getDrawable(R.drawable.ic_password)!!
+ context.getDrawable(R.drawable.ic_password) ?: return null,
+ preferImmediatelyAvailableCredentials = false,
)
is CreatePublicKeyCredentialRequest -> {
- val requestJson = createCredentialRequestJetpack.requestJson
- val json = JSONObject(requestJson)
- var name = ""
- var displayName = ""
- if (json.has("user")) {
- val user: JSONObject = json.getJSONObject("user")
- name = user.getString("name")
- displayName = user.getString("displayName")
- }
- RequestDisplayInfo(
- name,
- displayName,
- CredentialType.PASSKEY,
- appLabel,
- context.getDrawable(R.drawable.ic_passkey)!!
+ newRequestDisplayInfoFromPasskeyJson(
+ requestJson = createCredentialRequestJetpack.requestJson,
+ appLabel = appLabel,
+ context = context,
+ preferImmediatelyAvailableCredentials =
+ createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
+ )
+ }
+ is CreatePublicKeyCredentialRequestPrivileged -> {
+ newRequestDisplayInfoFromPasskeyJson(
+ requestJson = createCredentialRequestJetpack.requestJson,
+ appLabel = appLabel,
+ context = context,
+ preferImmediatelyAvailableCredentials =
+ createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
)
}
is CreateCustomCredentialRequest -> {
@@ -441,7 +468,8 @@ class CreateFlowUtils {
type = CredentialType.UNKNOWN,
appName = appLabel,
typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context)
- ?: context.getDrawable(R.drawable.ic_other_sign_in)!!
+ ?: context.getDrawable(R.drawable.ic_other_sign_in) ?: return null,
+ preferImmediatelyAvailableCredentials = false,
)
}
else -> null
@@ -608,5 +636,29 @@ class CreateFlowUtils {
)
} else null
}
+
+ private fun newRequestDisplayInfoFromPasskeyJson(
+ requestJson: String,
+ appLabel: String,
+ context: Context,
+ preferImmediatelyAvailableCredentials: Boolean,
+ ): RequestDisplayInfo? {
+ val json = JSONObject(requestJson)
+ var name = ""
+ var displayName = ""
+ if (json.has("user")) {
+ val user: JSONObject = json.getJSONObject("user")
+ name = user.getString("name")
+ displayName = user.getString("displayName")
+ }
+ return RequestDisplayInfo(
+ name,
+ displayName,
+ CredentialType.PASSKEY,
+ appLabel,
+ context.getDrawable(R.drawable.ic_passkey) ?: return null,
+ preferImmediatelyAvailableCredentials,
+ )
+ }
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
index eb3d188eab07..9216429eac52 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -29,6 +29,8 @@ import android.provider.Settings
import androidx.credentials.provider.CreateEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteCreateEntry
+import androidx.credentials.provider.RemoteCredentialEntry
import java.time.Instant
@@ -69,6 +71,21 @@ class GetTestUtils {
)
}
+ internal fun newRemoteCredentialEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ ): Entry {
+ val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ return Entry(
+ key,
+ subkey,
+ RemoteCredentialEntry(pendingIntent).slice
+ )
+ }
+
internal fun newActionEntry(
context: Context,
key: String,
@@ -203,5 +220,20 @@ class CreateTestUtils {
Intent()
)
}
+
+ internal fun newRemoteCreateEntry(
+ context: Context,
+ key: String,
+ subkey: String,
+ ): Entry {
+ val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ return Entry(
+ key,
+ subkey,
+ RemoteCreateEntry(pendingIntent).slice
+ )
+ }
}
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
index 6d07df70e7bf..297143309d14 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
@@ -24,8 +24,6 @@ enum class DialogState {
enum class ResultState {
COMPLETE,
- NORMAL_CANCELED,
- LAUNCH_SETTING_CANCELED
}
data class DialogResult(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
index 58edb25336f8..335d58ac85f6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/material/ModalBottomSheet.kt
@@ -451,7 +451,7 @@ private fun Modifier.bottomSheetSwipeable(
}
@Composable
-private fun Scrim(
+internal fun Scrim(
color: Color,
onDismiss: () -> Unit,
visible: Boolean
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
index 984057a1c960..04a2c07da388 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
@@ -16,6 +16,8 @@
package com.android.credentialmanager.common.ui
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
import com.android.credentialmanager.R
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
@@ -24,7 +26,6 @@ import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
@@ -32,17 +33,19 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
+import androidx.compose.ui.unit.dp
@Composable
fun ActionButton(text: String, onClick: () -> Unit) {
TextButton(
+ modifier = Modifier.padding(vertical = 4.dp),
onClick = onClick,
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.primary,
- )
+ ),
+ contentPadding = PaddingValues(start = 12.dp, top = 10.dp, end = 12.dp, bottom = 10.dp),
) {
- Text(text = text)
+ LargeLabelText(text = text)
}
}
@@ -64,7 +67,7 @@ fun ToggleVisibilityButton(modifier: Modifier = Modifier, onToggle: (Boolean) ->
contentDescription = if (toggleState.value)
stringResource(R.string.content_description_show_password) else
stringResource(R.string.content_description_hide_password),
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
index 85e5c1ee69d5..cc73089a96e7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
@@ -16,33 +16,77 @@
package com.android.credentialmanager.common.ui
-import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
/**
- * By default the card is filled with surfaceVariant color. This container card instead fills the
- * background color with surface corlor.
+ * Container card for the whole sheet.
+ *
+ * Surface 1 color. No vertical padding. 24dp horizontal padding. 24dp bottom padding. 24dp top
+ * padding if [topAppBar] is not present, and none otherwise.
+ */
+@Composable
+fun SheetContainerCard(
+ topAppBar: (@Composable () -> Unit)? = null,
+ modifier: Modifier = Modifier,
+ contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top,
+ content: LazyListScope.() -> Unit,
+) {
+ Card(
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
+ border = null,
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
+ ElevationTokens.Level1
+ ),
+ ),
+ ) {
+ if (topAppBar != null) {
+ topAppBar()
+ }
+ LazyColumn(
+ modifier = Modifier.padding(
+ start = 24.dp,
+ end = 24.dp,
+ bottom = 18.dp,
+ top = if (topAppBar == null) 24.dp else 0.dp
+ ).fillMaxWidth().wrapContentHeight(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ content = content,
+ verticalArrangement = contentVerticalArrangement,
+ )
+ }
+}
+
+/**
+ * Container card for the entries.
+ *
+ * Surface 3 color. No padding. Four rounded corner shape.
*/
@Composable
-fun ContainerCard(
+fun CredentialContainerCard(
modifier: Modifier = Modifier,
- shape: Shape = CardDefaults.shape,
- border: BorderStroke? = null,
content: @Composable ColumnScope.() -> Unit,
) {
Card(
- modifier = modifier.fillMaxWidth(),
- shape = shape,
- border = border,
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
+ shape = MaterialTheme.shapes.medium,
+ border = null,
colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surface,
+ containerColor = Color.Transparent,
),
content = content,
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt
new file mode 100644
index 000000000000..b2489fd57b5d
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.ui
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import kotlin.math.ln
+
+fun ColorScheme.surfaceColorAtElevation(elevation: Dp): Color {
+ if (elevation == 0.dp) return surface
+ val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
+ return surfaceTint.copy(alpha = alpha).compositeOver(surface)
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
index d8ee750a88d6..c09a692ce9fc 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt
@@ -16,21 +16,27 @@
package com.android.credentialmanager.common.ui
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+/** Primary container color; label-large button text; on-primary button text color. */
@Composable
fun ConfirmButton(text: String, onClick: () -> Unit) {
FilledTonalButton(
+ modifier = Modifier.padding(vertical = 4.dp),
onClick = onClick,
colors = ButtonDefaults.filledTonalButtonColors(
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- )
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ contentPadding = PaddingValues(start = 24.dp, top = 10.dp, end = 24.dp, bottom = 10.dp),
) {
- Text(text = text)
+ LargeLabelText(text = text)
}
} \ No newline at end of file
diff --git a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt
index a0813565b256..e1e666ef908b 100644
--- a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt
@@ -11,19 +11,19 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package android.window;
+package com.android.credentialmanager.common.ui
-import android.view.SurfaceControl;
+import androidx.compose.ui.unit.dp
-/**
- * Interface to be invoked when a windowless starting surface added.
- *
- * @param addedSurface The starting surface.
- * {@hide}
- */
-interface IWindowlessStartingSurfaceCallback {
- void onSurfaceAdded(in SurfaceControl addedSurface);
-}
+/** Copied from androidx.compose.material3.tokens. */
+internal object ElevationTokens {
+ val Level0 = 0.0.dp
+ val Level1 = 1.0.dp
+ val Level2 = 3.0.dp
+ val Level3 = 6.0.dp
+ val Level4 = 8.0.dp
+ val Level5 = 12.0.dp
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index aefd534da63c..0eaaf970f37f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -16,55 +16,314 @@
package com.android.credentialmanager.common.ui
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.SuggestionChipDefaults
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.R
import com.android.credentialmanager.ui.theme.EntryShape
+import com.android.credentialmanager.ui.theme.Shapes
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Entry(
- onClick: () -> Unit,
- label: @Composable () -> Unit,
modifier: Modifier = Modifier,
- icon: @Composable (() -> Unit)? = null,
+ onClick: () -> Unit,
+ entryHeadlineText: String,
+ entrySecondLineText: String? = null,
+ entryThirdLineText: String? = null,
+ /** Supply one and only one of the [iconImageBitmap], [iconImageVector], or [iconPainter] for
+ * drawing the leading icon. */
+ iconImageBitmap: ImageBitmap? = null,
+ shouldApplyIconImageBitmapTint: Boolean = false,
+ iconImageVector: ImageVector? = null,
+ iconPainter: Painter? = null,
+ /** This will replace the [entrySecondLineText] value and render the text along with a
+ * mask on / off toggle for hiding / displaying the password value. */
+ passwordValue: String? = null,
+ /** If true, draws a trailing lock icon. */
+ isLockedAuthEntry: Boolean = false,
) {
SuggestionChip(
- modifier = modifier.fillMaxWidth(),
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
onClick = onClick,
shape = EntryShape.FullSmallRoundedCorner,
- label = label,
- icon = icon,
+ label = {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth().padding(all = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(modifier = Modifier.wrapContentSize()) {
+ SmallTitleText(entryHeadlineText)
+ if (passwordValue != null) {
+ Row(modifier = Modifier.fillMaxWidth()) {
+ val visualTransformation = remember { PasswordVisualTransformation() }
+ val originalPassword by remember {
+ mutableStateOf(passwordValue)
+ }
+ val displayedPassword = remember {
+ mutableStateOf(
+ visualTransformation.filter(
+ AnnotatedString(originalPassword)
+ ).text.text
+ )
+ }
+ BodySmallText(displayedPassword.value)
+ ToggleVisibilityButton(
+ modifier = Modifier.padding(start = 5.dp).size(24.dp),
+ onToggle = {
+ if (it) {
+ displayedPassword.value = originalPassword
+ } else {
+ displayedPassword.value = visualTransformation.filter(
+ AnnotatedString(originalPassword)
+ ).text.text
+ }
+ },
+ )
+ }
+ } else if (entrySecondLineText != null) {
+ BodySmallText(entrySecondLineText)
+ }
+ if (entryThirdLineText != null) {
+ BodySmallText(entryThirdLineText)
+ }
+ }
+ if (isLockedAuthEntry) {
+ Box(modifier = Modifier.wrapContentSize().padding(end = 16.dp)) {
+ Icon(
+ imageVector = Icons.Outlined.Lock,
+ // Decorative purpose only.
+ contentDescription = null,
+ modifier = Modifier.size(24.dp),
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
+ }
+ },
+ icon =
+ if (iconImageBitmap != null) {
+ if (shouldApplyIconImageBitmapTint) {
+ {
+ Box(modifier = Modifier.wrapContentSize()
+ .padding(start = 16.dp, top = 16.dp, bottom = 16.dp)) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ bitmap = iconImageBitmap,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+ }
+ } else {
+ {
+ Box(modifier = Modifier.wrapContentSize()
+ .padding(start = 16.dp, top = 16.dp, bottom = 16.dp)) {
+ Image(
+ modifier = Modifier.size(24.dp),
+ bitmap = iconImageBitmap,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+ }
+ }
+ } else if (iconImageVector != null) {
+ {
+ Box(modifier = Modifier.wrapContentSize()
+ .padding(start = 16.dp, top = 16.dp, bottom = 16.dp)) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ imageVector = iconImageVector,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+ }
+ } else if (iconPainter != null) {
+ {
+ Box(modifier = Modifier.wrapContentSize()
+ .padding(start = 16.dp, top = 16.dp, bottom = 16.dp)) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ painter = iconPainter,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+ }
+ } else {
+ null
+ },
border = null,
colors = SuggestionChipDefaults.suggestionChipColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant,
+ containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
+ ElevationTokens.Level3
+ ),
+ // TODO: remove?
labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
iconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
}
-@OptIn(ExperimentalMaterial3Api::class)
+/**
+ * A variation of the normal entry in that its background is transparent and the paddings are
+ * different (no horizontal padding).
+ */
@Composable
-fun TransparentBackgroundEntry(
+fun ActionEntry(
onClick: () -> Unit,
- label: @Composable () -> Unit,
- modifier: Modifier = Modifier,
- icon: @Composable (() -> Unit)? = null,
+ entryHeadlineText: String,
+ entrySecondLineText: String? = null,
+ iconImageBitmap: ImageBitmap,
) {
SuggestionChip(
- modifier = modifier.fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
onClick = onClick,
- label = label,
- icon = icon,
+ shape = Shapes.large,
+ label = {
+ Column(modifier = Modifier.wrapContentSize()
+ .padding(start = 16.dp, top = 16.dp, bottom = 16.dp)) {
+ SmallTitleText(entryHeadlineText)
+ if (entrySecondLineText != null) {
+ BodySmallText(entrySecondLineText)
+ }
+ }
+ },
+ icon = {
+ Box(modifier = Modifier.wrapContentSize().padding(vertical = 16.dp)) {
+ Image(
+ modifier = Modifier.size(24.dp),
+ bitmap = iconImageBitmap,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+ },
border = null,
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = Color.Transparent,
),
)
+}
+
+/**
+ * A single row of leading icon and text describing a benefit of passkeys, used by the
+ * [com.android.credentialmanager.createflow.PasskeyIntroCard].
+ */
+@Composable
+fun PasskeyBenefitRow(
+ leadingIconPainter: Painter,
+ text: String,
+) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ painter = leadingIconPainter,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ BodyMediumText(text = text)
+ }
+}
+
+/**
+ * A single row of one or two CTA buttons for continuing or cancelling the current step.
+ */
+@Composable
+fun CtaButtonRow(
+ leftButton: (@Composable () -> Unit)? = null,
+ rightButton: (@Composable () -> Unit)? = null,
+) {
+ Row(
+ horizontalArrangement =
+ if (leftButton == null) Arrangement.End
+ else if (rightButton == null) Arrangement.Start
+ else Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ if (leftButton != null) {
+ leftButton()
+ }
+ if (rightButton != null) {
+ rightButton()
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MoreOptionTopAppBar(
+ text: String,
+ onNavigationIconClicked: () -> Unit,
+) {
+ TopAppBar(
+ title = {
+ LargeTitleText(text = text, modifier = Modifier.padding(horizontal = 4.dp))
+ },
+ navigationIcon = {
+ IconButton(
+ modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp),
+ onClick = onNavigationIconClicked
+ ) {
+ Box(
+ modifier = Modifier.size(48.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = stringResource(
+ R.string.accessibility_back_arrow_button
+ ),
+ modifier = Modifier.size(16.dp),
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
+ },
+ colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
+ modifier = Modifier.padding(top = 12.dp, bottom = 8.dp)
+ )
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/HeadlineIcon.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/HeadlineIcon.kt
new file mode 100644
index 000000000000..ac79844457a8
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/HeadlineIcon.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.ui
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+
+/** Tinted primary; centered; 32X32. */
+@Composable
+fun HeadlineIcon(bitmap: ImageBitmap, tint: Color? = null) {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ ) {
+ Icon(
+ modifier = Modifier.size(32.dp),
+ bitmap = bitmap,
+ tint = tint ?: MaterialTheme.colorScheme.primary,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+}
+
+@Composable
+fun HeadlineIcon(imageVector: ImageVector) {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ ) {
+ Icon(
+ modifier = Modifier.size(32.dp),
+ imageVector = imageVector,
+ tint = MaterialTheme.colorScheme.primary,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+}
+
+@Composable
+fun HeadlineIcon(painter: Painter) {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ ) {
+ Icon(
+ modifier = Modifier.size(32.dp),
+ painter = painter,
+ tint = MaterialTheme.colorScheme.primary,
+ // Decorative purpose only.
+ contentDescription = null,
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
new file mode 100644
index 000000000000..c63771e49516
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.ui
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun CredentialListSectionHeader(text: String) {
+ InternalSectionHeader(text, MaterialTheme.colorScheme.onSurfaceVariant)
+}
+
+@Composable
+fun MoreAboutPasskeySectionHeader(text: String) {
+ InternalSectionHeader(text, MaterialTheme.colorScheme.onSurface)
+}
+
+@Composable
+private fun InternalSectionHeader(text: String, color: Color) {
+ Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+ SectionHeaderText(
+ text,
+ modifier = Modifier.padding(top = 20.dp, bottom = 8.dp),
+ color = color
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
new file mode 100644
index 000000000000..514ff90be8d7
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.credentialmanager.R
+import com.android.credentialmanager.common.material.Scrim
+import com.android.credentialmanager.ui.theme.Shapes
+
+@Composable
+fun Snackbar(
+ contentText: String,
+ action: (@Composable () -> Unit)? = null,
+ onDismiss: () -> Unit,
+) {
+ BoxWithConstraints {
+ Box(Modifier.fillMaxSize()) {
+ Scrim(
+ color = Color.Transparent,
+ onDismiss = onDismiss,
+ visible = true
+ )
+ }
+ Box(
+ modifier = Modifier
+ .align(Alignment.BottomCenter).wrapContentSize().padding(bottom = 18.dp)
+ ) {
+ Card(
+ shape = Shapes.medium,
+ modifier = Modifier.wrapContentSize(),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.inverseSurface,
+ )
+ ) {
+ Row(
+ modifier = Modifier.wrapContentSize(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ SnackbarContentText(contentText, modifier = Modifier.padding(
+ top = 18.dp, bottom = 18.dp, start = 24.dp,
+ ))
+ if (action != null) {
+ action()
+ }
+ IconButton(onClick = onDismiss, modifier = Modifier.padding(
+ top = 4.dp, bottom = 4.dp, start = 2.dp, end = 10.dp,
+ )) {
+ Icon(
+ Icons.Filled.Close,
+ contentDescription = stringResource(
+ R.string.accessibility_snackbar_dismiss
+ ),
+ tint = MaterialTheme.colorScheme.inverseOnSurface,
+ )
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 3a66dda73832..8af729ecdc25 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -16,69 +16,144 @@
package com.android.credentialmanager.common.ui
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
+/**
+ * The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
+ *
+ * Centered horizontally; headline-small typography; on-surface color.
+ */
@Composable
-fun TextOnSurface(
- text: String,
- modifier: Modifier = Modifier,
- textAlign: TextAlign? = null,
- style: TextStyle,
-) {
- TextInternal(
+fun HeadlineText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
text = text,
color = MaterialTheme.colorScheme.onSurface,
- modifier = modifier,
- textAlign = textAlign,
- style = style,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.headlineSmall,
)
}
+/**
+ * Body-medium typography; on-surface-variant color.
+ */
@Composable
-fun TextSecondary(
- text: String,
- modifier: Modifier = Modifier,
- textAlign: TextAlign? = null,
- style: TextStyle,
-) {
- TextInternal(
+fun BodyMediumText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
text = text,
- color = MaterialTheme.colorScheme.secondary,
- modifier = modifier,
- textAlign = textAlign,
- style = style,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
)
}
+/**
+ * Body-small typography; on-surface-variant color.
+ */
@Composable
-fun TextOnSurfaceVariant(
- text: String,
- modifier: Modifier = Modifier,
- textAlign: TextAlign? = null,
- style: TextStyle,
-) {
- TextInternal(
+fun BodySmallText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
text = text,
color = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = modifier,
- textAlign = textAlign,
- style = style,
+ style = MaterialTheme.typography.bodySmall,
+ )
+}
+
+/**
+ * Title-large typography; on-surface color.
+ */
+@Composable
+fun LargeTitleText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleLarge,
)
}
+/**
+ * Title-small typography; on-surface color.
+ */
+@Composable
+fun SmallTitleText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleSmall,
+ )
+}
+
+/**
+ * Title-small typography.
+ */
@Composable
-private fun TextInternal(
- text: String,
- color: Color,
- modifier: Modifier,
- textAlign: TextAlign?,
- style: TextStyle,
-) {
- Text(text = text, color = color, modifier = modifier, textAlign = textAlign, style = style)
+fun SectionHeaderText(text: String, modifier: Modifier = Modifier, color: Color) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = color,
+ style = MaterialTheme.typography.titleSmall,
+ )
+}
+
+/**
+ * Body-medium typography; inverse-on-surface color.
+ */
+@Composable
+fun SnackbarContentText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = MaterialTheme.colorScheme.inverseOnSurface,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+}
+
+/**
+ * Label-large typography; inverse-primary color.
+ */
+@Composable
+fun SnackbarActionText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ color = MaterialTheme.colorScheme.inversePrimary,
+ style = MaterialTheme.typography.labelLarge,
+ )
+}
+
+/**
+ * Label-large typography; on-surface-variant color; centered.
+ */
+@Composable
+fun LargeLabelTextOnSurfaceVariant(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ textAlign = TextAlign.Center,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.labelLarge,
+ )
+}
+
+/**
+ * Label-large typography; color following parent spec; centered.
+ */
+@Composable
+fun LargeLabelText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ modifier = modifier.wrapContentSize(),
+ text = text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelLarge,
+ )
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 942eb490aa20..00b7d0057746 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -11,36 +11,24 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.TopAppBar
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
@@ -49,15 +37,20 @@ import com.android.credentialmanager.common.BaseEntry
import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.ui.ActionButton
+import com.android.credentialmanager.common.ui.BodyMediumText
+import com.android.credentialmanager.common.ui.BodySmallText
import com.android.credentialmanager.common.ui.ConfirmButton
+import com.android.credentialmanager.common.ui.CredentialContainerCard
+import com.android.credentialmanager.common.ui.CtaButtonRow
import com.android.credentialmanager.common.ui.Entry
+import com.android.credentialmanager.common.ui.HeadlineIcon
+import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ModalBottomSheet
-import com.android.credentialmanager.common.ui.TextOnSurface
-import com.android.credentialmanager.common.ui.TextSecondary
-import com.android.credentialmanager.common.ui.TextOnSurfaceVariant
-import com.android.credentialmanager.common.ui.ContainerCard
-import com.android.credentialmanager.common.ui.ToggleVisibilityButton
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
+import com.android.credentialmanager.common.ui.MoreAboutPasskeySectionHeader
+import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
+import com.android.credentialmanager.common.ui.SheetContainerCard
+import com.android.credentialmanager.common.ui.PasskeyBenefitRow
+import com.android.credentialmanager.common.ui.HeadlineText
@Composable
fun CreateCredentialScreen(
@@ -73,7 +66,7 @@ fun CreateCredentialScreen(
when (viewModel.uiState.providerActivityState) {
ProviderActivityState.NOT_APPLICABLE -> {
when (createCredentialUiState.currentScreenState) {
- CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
+ CreateScreenState.PASSKEY_INTRO -> PasskeyIntroCard(
onConfirm = viewModel::createFlowOnConfirmIntro,
onLearnMore = viewModel::createFlowOnLearnMore,
)
@@ -157,14 +150,13 @@ fun CreateCredentialScreen(
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun ConfirmationCard(
+fun PasskeyIntroCard(
onConfirm: () -> Unit,
onLearnMore: () -> Unit,
) {
- ContainerCard() {
- Column() {
+ SheetContainerCard {
+ item {
val onboardingImageResource = remember {
mutableStateOf(R.drawable.ic_passkeys_onboarding)
}
@@ -173,101 +165,56 @@ fun ConfirmationCard(
} else {
onboardingImageResource.value = R.drawable.ic_passkeys_onboarding
}
- Image(
- painter = painterResource(onboardingImageResource.value),
- contentDescription = null,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- .padding(top = 24.dp, bottom = 12.dp).size(316.dp, 168.dp)
- )
- TextOnSurface(
- text = stringResource(R.string.passkey_creation_intro_title),
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier
- .padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
- )
- Divider(
- thickness = 16.dp,
- color = Color.Transparent
- )
Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ modifier = Modifier.wrapContentHeight().fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center,
) {
Image(
- modifier = Modifier.size(24.dp),
- painter = painterResource(R.drawable.ic_passkeys_onboarding_password),
- contentDescription = null
- )
- TextSecondary(
- text = stringResource(R.string.passkey_creation_intro_body_password),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(start = 16.dp, end = 4.dp),
+ painter = painterResource(onboardingImageResource.value),
+ contentDescription = null,
+ modifier = Modifier.size(316.dp, 168.dp)
)
}
- Divider(
- thickness = 16.dp,
- color = Color.Transparent
+ }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ PasskeyBenefitRow(
+ leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password),
+ text = stringResource(R.string.passkey_creation_intro_body_password),
)
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- Image(
- modifier = Modifier.size(24.dp),
- painter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint),
- contentDescription = null
- )
- TextSecondary(
- text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(start = 16.dp, end = 4.dp),
- )
- }
- Divider(
- thickness = 16.dp,
- color = Color.Transparent
+ }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ PasskeyBenefitRow(
+ leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint),
+ text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
)
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- Image(
- modifier = Modifier.size(24.dp),
- painter = painterResource(R.drawable.ic_passkeys_onboarding_device),
- contentDescription = null
- )
- TextSecondary(
- text = stringResource(R.string.passkey_creation_intro_body_device),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(start = 16.dp, end = 4.dp),
- )
- }
- Divider(
- thickness = 32.dp,
- color = Color.Transparent
+ }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ PasskeyBenefitRow(
+ leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device),
+ text = stringResource(R.string.passkey_creation_intro_body_device),
)
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- ActionButton(
- stringResource(R.string.string_learn_more),
- onClick = onLearnMore
- )
- ConfirmButton(
- stringResource(R.string.string_continue),
- onClick = onConfirm
- )
- }
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 18.dp)
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+
+ item {
+ CtaButtonRow(
+ leftButton = {
+ ActionButton(
+ stringResource(R.string.string_learn_more),
+ onClick = onLearnMore
+ )
+ },
+ rightButton = {
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
+ )
+ },
)
}
}
@@ -283,16 +230,11 @@ fun ProviderSelectionCard(
onDisabledProvidersSelected: () -> Unit,
onMoreOptionsSelected: () -> Unit,
) {
- ContainerCard() {
- Column() {
- Icon(
- bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- .padding(top = 24.dp, bottom = 16.dp).size(32.dp)
- )
- TextOnSurface(
+ SheetContainerCard {
+ item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ HeadlineText(
text = stringResource(
R.string.choose_provider_title,
when (requestDisplayInfo.type) {
@@ -302,83 +244,56 @@ fun ProviderSelectionCard(
stringResource(R.string.passwords)
CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
}
- ),
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
- )
- Divider(
- thickness = 16.dp,
- color = Color.Transparent
- )
- TextSecondary(
- text = stringResource(R.string.choose_provider_body),
- style = MaterialTheme.typography.bodyLarge,
- modifier = Modifier.padding(horizontal = 28.dp),
+ )
)
- ContainerCard(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier.padding(
- start = 24.dp,
- end = 24.dp,
- top = 24.dp,
- bottom = if (hasRemoteEntry) 24.dp else 16.dp
- ).align(alignment = Alignment.CenterHorizontally),
- ) {
- LazyColumn(
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+
+ item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
+ Column(
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
sortedCreateOptionsPairs.forEach { entry ->
- item {
- MoreOptionsInfoRow(
- requestDisplayInfo = requestDisplayInfo,
- providerInfo = entry.second,
- createOptionInfo = entry.first,
- onOptionSelected = {
- onOptionSelected(
- ActiveEntry(
- entry.second,
- entry.first
- )
+ MoreOptionsInfoRow(
+ requestDisplayInfo = requestDisplayInfo,
+ providerInfo = entry.second,
+ createOptionInfo = entry.first,
+ onOptionSelected = {
+ onOptionSelected(
+ ActiveEntry(
+ entry.second,
+ entry.first
)
- }
- )
- }
- }
- item {
- MoreOptionsDisabledProvidersRow(
- disabledProviders = disabledProviderList,
- onDisabledProvidersSelected =
- onDisabledProvidersSelected,
+ )
+ }
)
}
+ MoreOptionsDisabledProvidersRow(
+ disabledProviders = disabledProviderList,
+ onDisabledProvidersSelected = onDisabledProvidersSelected,
+ )
}
}
- if (hasRemoteEntry) {
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
+ }
+ if (hasRemoteEntry) {
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CtaButtonRow(
+ leftButton = {
+ ActionButton(
+ stringResource(R.string.string_more_options),
+ onMoreOptionsSelected
+ )
+ }
)
- Row(
- horizontalArrangement = Arrangement.Start,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- ActionButton(
- stringResource(R.string.string_more_options),
- onMoreOptionsSelected
- )
- }
}
- Divider(
- thickness = 24.dp,
- color = Color.Transparent,
- )
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
@@ -393,158 +308,104 @@ fun MoreOptionsSelectionCard(
onDisabledProvidersSelected: () -> Unit,
onRemoteEntrySelected: (BaseEntry) -> Unit,
) {
- ContainerCard() {
- Column() {
- TopAppBar(
- title = {
- TextOnSurface(
- text =
- stringResource(
- R.string.save_credential_to_title,
- when (requestDisplayInfo.type) {
- CredentialType.PASSKEY ->
- stringResource(R.string.passkey)
- CredentialType.PASSWORD ->
- stringResource(R.string.password)
- CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
- }
- ),
- style = MaterialTheme.typography.titleMedium,
- )
- },
- navigationIcon = {
- IconButton(
- onClick =
- if (isFromProviderSelection)
- onBackProviderSelectionButtonSelected
- else onBackCreationSelectionButtonSelected
- ) {
- Icon(
- Icons.Filled.ArrowBack,
- stringResource(R.string.accessibility_back_arrow_button)
- )
- }
- },
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
- modifier = Modifier.padding(top = 12.dp)
- )
- Divider(
- thickness = 8.dp,
- color = Color.Transparent
- )
- ContainerCard(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier
- .padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally)
- ) {
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(2.dp)
- ) {
+ SheetContainerCard(topAppBar = {
+ MoreOptionTopAppBar(
+ text = stringResource(
+ R.string.save_credential_to_title,
+ when (requestDisplayInfo.type) {
+ CredentialType.PASSKEY ->
+ stringResource(R.string.passkey)
+ CredentialType.PASSWORD ->
+ stringResource(R.string.password)
+ CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info)
+ }
+ ),
+ onNavigationIconClicked =
+ if (isFromProviderSelection) onBackProviderSelectionButtonSelected
+ else onBackCreationSelectionButtonSelected,
+ )
+ }) {
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
+ Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
// Only in the flows with default provider(not first time use) we can show the
// createOptions here, or they will be shown on ProviderSelectionCard
if (hasDefaultProvider) {
sortedCreateOptionsPairs.forEach { entry ->
- item {
- MoreOptionsInfoRow(
- requestDisplayInfo = requestDisplayInfo,
- providerInfo = entry.second,
- createOptionInfo = entry.first,
- onOptionSelected = {
- onOptionSelected(
- ActiveEntry(
- entry.second,
- entry.first
- )
+ MoreOptionsInfoRow(
+ requestDisplayInfo = requestDisplayInfo,
+ providerInfo = entry.second,
+ createOptionInfo = entry.first,
+ onOptionSelected = {
+ onOptionSelected(
+ ActiveEntry(
+ entry.second,
+ entry.first
)
- })
- }
- }
- item {
- MoreOptionsDisabledProvidersRow(
- disabledProviders = disabledProviderList,
- onDisabledProvidersSelected =
- onDisabledProvidersSelected,
+ )
+ }
)
}
+ MoreOptionsDisabledProvidersRow(
+ disabledProviders = disabledProviderList,
+ onDisabledProvidersSelected =
+ onDisabledProvidersSelected,
+ )
}
enabledProviderList.forEach {
if (it.remoteEntry != null) {
- item {
- RemoteEntryRow(
- remoteInfo = it.remoteEntry!!,
- onRemoteEntrySelected = onRemoteEntrySelected,
- )
- }
+ RemoteEntryRow(
+ remoteInfo = it.remoteEntry!!,
+ onRemoteEntrySelected = onRemoteEntrySelected,
+ )
return@forEach
}
}
}
}
- Divider(
- thickness = 8.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 40.dp)
- )
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsRowIntroCard(
providerInfo: EnabledProviderInfo,
onChangeDefaultSelected: () -> Unit,
onUseOnceSelected: () -> Unit,
) {
- ContainerCard() {
- Column() {
- Icon(
- Icons.Outlined.NewReleases,
- contentDescription = null,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- .padding(all = 24.dp),
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- )
- TextOnSurface(
+ SheetContainerCard {
+ item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ HeadlineText(
text = stringResource(
R.string.use_provider_for_all_title,
providerInfo.displayName
- ),
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
- )
- TextSecondary(
- text = stringResource(R.string.use_provider_for_all_description),
- style = MaterialTheme.typography.bodyLarge,
- modifier = Modifier.padding(all = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- )
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- ActionButton(
- stringResource(R.string.use_once),
- onClick = onUseOnceSelected
- )
- ConfirmButton(
- stringResource(R.string.set_as_default),
- onClick = onChangeDefaultSelected
)
- }
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 40.dp)
+ )
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) }
+ item {
+ CtaButtonRow(
+ leftButton = {
+ ActionButton(
+ stringResource(R.string.use_once),
+ onClick = onUseOnceSelected
+ )
+ },
+ rightButton = {
+ ConfirmButton(
+ stringResource(R.string.set_as_default),
+ onClick = onChangeDefaultSelected
+ )
+ },
)
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreationSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
@@ -556,26 +417,18 @@ fun CreationSelectionCard(
onMoreOptionsSelected: () -> Unit,
hasDefaultProvider: Boolean,
) {
- ContainerCard() {
- Column() {
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- Icon(
+ SheetContainerCard {
+ item {
+ HeadlineIcon(
bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
- contentDescription = null,
tint = Color.Unspecified,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally).size(32.dp)
- )
- TextSecondary(
- text = providerInfo.displayName,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(vertical = 10.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
)
- TextOnSurface(
+ }
+ item { Divider(thickness = 4.dp, color = Color.Transparent) }
+ item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item {
+ HeadlineText(
text = when (requestDisplayInfo.type) {
CredentialType.PASSKEY -> stringResource(
R.string.choose_create_option_passkey_title,
@@ -589,80 +442,67 @@ fun CreationSelectionCard(
R.string.choose_create_option_sign_in_title,
requestDisplayInfo.appName
)
- },
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
+ }
)
- ContainerCard(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier
- .padding(all = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- ) {
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
PrimaryCreateOptionRow(
requestDisplayInfo = requestDisplayInfo,
entryInfo = createOptionInfo,
onOptionSelected = onOptionSelected
)
}
- var createOptionsSize = 0
- var remoteEntry: RemoteInfo? = null
- enabledProviderList.forEach { enabledProvider ->
- if (enabledProvider.remoteEntry != null) {
- remoteEntry = enabledProvider.remoteEntry
- }
- createOptionsSize += enabledProvider.createOptions.size
- }
- val shouldShowMoreOptionsButton = if (!hasDefaultProvider) {
- // User has already been presented with all options on the default provider
- // selection screen. Don't show them again. Therefore, only show the more option
- // button if remote option is present.
- remoteEntry != null
- } else {
- createOptionsSize > 1 || remoteEntry != null
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ var createOptionsSize = 0
+ var remoteEntry: RemoteInfo? = null
+ enabledProviderList.forEach { enabledProvider ->
+ if (enabledProvider.remoteEntry != null) {
+ remoteEntry = enabledProvider.remoteEntry
}
- Row(
- horizontalArrangement =
- if (shouldShowMoreOptionsButton) Arrangement.SpaceBetween else Arrangement.End,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- if (shouldShowMoreOptionsButton) {
- ActionButton(
- stringResource(R.string.string_more_options),
- onClick = onMoreOptionsSelected
+ createOptionsSize += enabledProvider.createOptions.size
+ }
+ val shouldShowMoreOptionsButton = if (!hasDefaultProvider) {
+ // User has already been presented with all options on the default provider
+ // selection screen. Don't show them again. Therefore, only show the more option
+ // button if remote option is present.
+ remoteEntry != null
+ } else {
+ createOptionsSize > 1 || remoteEntry != null
+ }
+ item {
+ CtaButtonRow(
+ leftButton = if (shouldShowMoreOptionsButton) {
+ {
+ ActionButton(
+ stringResource(R.string.string_more_options),
+ onMoreOptionsSelected
+ )
+ }
+ } else null,
+ rightButton = {
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
)
- }
- ConfirmButton(
- stringResource(R.string.string_continue),
- onClick = onConfirm
- )
- }
- if (createOptionInfo.footerDescription != null) {
+ },
+ )
+ }
+ if (createOptionInfo.footerDescription != null) {
+ item {
Divider(
thickness = 1.dp,
- color = Color.LightGray,
- modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 18.dp)
- )
- TextSecondary(
- text = createOptionInfo.footerDescription,
- style = MaterialTheme.typography.bodyLarge,
- modifier = Modifier.padding(
- start = 29.dp, top = 8.dp, bottom = 18.dp, end = 28.dp
- )
+ color = MaterialTheme.colorScheme.outlineVariant,
+ modifier = Modifier.padding(vertical = 16.dp)
)
}
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 16.dp)
- )
+ item { BodySmallText(text = createOptionInfo.footerDescription) }
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExternalOnlySelectionCard(
requestDisplayInfo: RequestDisplayInfo,
@@ -670,148 +510,74 @@ fun ExternalOnlySelectionCard(
onOptionSelected: (BaseEntry) -> Unit,
onConfirm: () -> Unit,
) {
- ContainerCard() {
- Column() {
- Icon(
- painter = painterResource(R.drawable.ic_other_devices),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
- .padding(all = 24.dp).size(32.dp)
- )
- TextOnSurface(
- text = stringResource(R.string.create_passkey_in_other_device_title),
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- textAlign = TextAlign.Center,
- )
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- ContainerCard(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier
- .padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- ) {
+ SheetContainerCard {
+ item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
+ item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
PrimaryCreateOptionRow(
requestDisplayInfo = requestDisplayInfo,
entryInfo = activeRemoteEntry,
onOptionSelected = onOptionSelected
)
}
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- Row(
- horizontalArrangement = Arrangement.End,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- ConfirmButton(
- stringResource(R.string.string_continue),
- onClick = onConfirm
- )
- }
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 16.dp)
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CtaButtonRow(
+ rightButton = {
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
+ )
+ },
)
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreAboutPasskeysIntroCard(
onBackPasskeyIntroButtonSelected: () -> Unit,
) {
- ContainerCard() {
- Column() {
- TopAppBar(
- title = {
- TextOnSurface(
- text =
- stringResource(
- R.string.more_about_passkeys_title
- ),
- style = MaterialTheme.typography.titleMedium,
- )
- },
- navigationIcon = {
- IconButton(
- onClick = onBackPasskeyIntroButtonSelected
- ) {
- Icon(
- Icons.Filled.ArrowBack,
- stringResource(R.string.accessibility_back_arrow_button)
- )
- }
- },
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
- modifier = Modifier.padding(top = 12.dp)
+ SheetContainerCard(
+ topAppBar = {
+ MoreOptionTopAppBar(
+ text = stringResource(R.string.more_about_passkeys_title),
+ onNavigationIconClicked = onBackPasskeyIntroButtonSelected,
)
- Column(
- modifier = Modifier.fillMaxWidth().padding(start = 24.dp, end = 68.dp)
- ) {
- TextOnSurfaceVariant(
- text = stringResource(R.string.passwordless_technology_title),
- style = MaterialTheme.typography.titleLarge,
- )
- TextSecondary(
- text = stringResource(R.string.passwordless_technology_detail),
- style = MaterialTheme.typography.bodyMedium,
- )
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- TextOnSurfaceVariant(
- text = stringResource(R.string.public_key_cryptography_title),
- style = MaterialTheme.typography.titleLarge,
- )
- TextSecondary(
- text = stringResource(R.string.public_key_cryptography_detail),
- style = MaterialTheme.typography.bodyMedium,
- )
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- TextOnSurfaceVariant(
- text = stringResource(R.string.improved_account_security_title),
- style = MaterialTheme.typography.titleLarge,
- )
- TextSecondary(
- text = stringResource(R.string.improved_account_security_detail),
- style = MaterialTheme.typography.bodyMedium,
- )
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- TextOnSurfaceVariant(
- text = stringResource(R.string.seamless_transition_title),
- style = MaterialTheme.typography.titleLarge,
- )
- TextSecondary(
- text = stringResource(R.string.seamless_transition_detail),
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 24.dp)
+ },
+ contentVerticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ item {
+ MoreAboutPasskeySectionHeader(
+ text = stringResource(R.string.passwordless_technology_title)
+ )
+ BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
+ }
+ item {
+ MoreAboutPasskeySectionHeader(
+ text = stringResource(R.string.public_key_cryptography_title)
)
+ BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
+ }
+ item {
+ MoreAboutPasskeySectionHeader(
+ text = stringResource(R.string.improved_account_security_title)
+ )
+ BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
+ }
+ item {
+ MoreAboutPasskeySectionHeader(
+ text = stringResource(R.string.seamless_transition_title)
+ )
+ BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
@@ -820,115 +586,37 @@ fun PrimaryCreateOptionRow(
) {
Entry(
onClick = { onOptionSelected(entryInfo) },
- icon = {
- if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
- Image(
- bitmap = entryInfo.profileIcon.toBitmap().asImageBitmap(),
- contentDescription = null,
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- )
- } else {
- Icon(
- bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- )
- }
+ iconImageBitmap =
+ if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
+ entryInfo.profileIcon.toBitmap().asImageBitmap()
+ } else {
+ requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
},
- label = {
- Column() {
- when (requestDisplayInfo.type) {
- CredentialType.PASSKEY -> {
- TextOnSurfaceVariant(
- text = requestDisplayInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp),
- )
- TextSecondary(
- text = if (requestDisplayInfo.subtitle != null) {
- requestDisplayInfo.subtitle + " • " + stringResource(
- R.string.passkey_before_subtitle
- )
- } else {
- stringResource(R.string.passkey_before_subtitle)
- },
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- }
- CredentialType.PASSWORD -> {
- TextOnSurfaceVariant(
- text = requestDisplayInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp),
- )
- Row(
- modifier = Modifier.fillMaxWidth().padding(
- top = 4.dp, bottom = 16.dp,
- start = 5.dp
- ),
- verticalAlignment = Alignment.CenterVertically
- ) {
- val visualTransformation = remember { PasswordVisualTransformation() }
- // This subtitle would never be null for create password
- val originalPassword by remember {
- mutableStateOf(requestDisplayInfo.subtitle ?: "")
- }
- val displayedPassword = remember {
- mutableStateOf(
- visualTransformation.filter(
- AnnotatedString(originalPassword)
- ).text.text
- )
- }
- TextSecondary(
- text = displayedPassword.value,
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(top = 4.dp, bottom = 4.dp),
- )
-
- ToggleVisibilityButton(modifier = Modifier.padding(start = 4.dp)
- .height(24.dp).width(24.dp), onToggle = {
- if (it) {
- displayedPassword.value = originalPassword
- } else {
- displayedPassword.value = visualTransformation.filter(
- AnnotatedString(originalPassword)
- ).text.text
- }
- })
- }
- }
- CredentialType.UNKNOWN -> {
- if (requestDisplayInfo.subtitle != null) {
- TextOnSurfaceVariant(
- text = requestDisplayInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp),
- )
- TextOnSurfaceVariant(
- text = requestDisplayInfo.subtitle,
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- } else {
- TextOnSurfaceVariant(
- text = requestDisplayInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(
- top = 16.dp, bottom = 16.dp, start = 5.dp
- ),
- )
- }
- }
+ shouldApplyIconImageBitmapTint = !(entryInfo is CreateOptionInfo &&
+ entryInfo.profileIcon != null),
+ entryHeadlineText = requestDisplayInfo.title,
+ entrySecondLineText = when (requestDisplayInfo.type) {
+ CredentialType.PASSKEY -> {
+ if (requestDisplayInfo.subtitle != null) {
+ requestDisplayInfo.subtitle + " • " + stringResource(
+ R.string.passkey_before_subtitle
+ )
+ } else {
+ stringResource(R.string.passkey_before_subtitle)
}
}
- }
+ // Set passwordValue instead
+ CredentialType.PASSWORD -> null
+ CredentialType.UNKNOWN -> requestDisplayInfo.subtitle
+ },
+ passwordValue =
+ if (requestDisplayInfo.type == CredentialType.PASSWORD)
+ // This subtitle would never be null for create password
+ requestDisplayInfo.subtitle ?: ""
+ else null,
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsInfoRow(
requestDisplayInfo: RequestDisplayInfo,
@@ -938,93 +626,46 @@ fun MoreOptionsInfoRow(
) {
Entry(
onClick = onOptionSelected,
- icon = {
- Image(
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
- contentDescription = null
- )
- },
- label = {
- Column() {
- TextOnSurfaceVariant(
- text = providerInfo.displayName,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp),
+ iconImageBitmap = providerInfo.icon.toBitmap().asImageBitmap(),
+ entryHeadlineText = providerInfo.displayName,
+ entrySecondLineText = createOptionInfo.userProviderDisplayName,
+ entryThirdLineText =
+ if (requestDisplayInfo.type == CredentialType.PASSKEY ||
+ requestDisplayInfo.type == CredentialType.PASSWORD) {
+ if (createOptionInfo.passwordCount != null &&
+ createOptionInfo.passkeyCount != null
+ ) {
+ stringResource(
+ R.string.more_options_usage_passwords_passkeys,
+ createOptionInfo.passwordCount,
+ createOptionInfo.passkeyCount
)
- if (createOptionInfo.userProviderDisplayName != null) {
- TextSecondary(
- text = createOptionInfo.userProviderDisplayName,
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(start = 5.dp),
- )
- }
- if (requestDisplayInfo.type == CredentialType.PASSKEY ||
- requestDisplayInfo.type == CredentialType.PASSWORD
- ) {
- if (createOptionInfo.passwordCount != null &&
- createOptionInfo.passkeyCount != null
- ) {
- TextSecondary(
- text =
- stringResource(
- R.string.more_options_usage_passwords_passkeys,
- createOptionInfo.passwordCount,
- createOptionInfo.passkeyCount
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- } else if (createOptionInfo.passwordCount != null) {
- TextSecondary(
- text =
- stringResource(
- R.string.more_options_usage_passwords,
- createOptionInfo.passwordCount
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- } else if (createOptionInfo.passkeyCount != null) {
- TextSecondary(
- text =
- stringResource(
- R.string.more_options_usage_passkeys,
- createOptionInfo.passkeyCount
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- } else {
- Divider(
- thickness = 16.dp,
- color = Color.Transparent,
- )
- }
- } else {
- if (createOptionInfo.totalCredentialCount != null) {
- TextSecondary(
- text =
- stringResource(
- R.string.more_options_usage_credentials,
- createOptionInfo.totalCredentialCount
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- } else {
- Divider(
- thickness = 16.dp,
- color = Color.Transparent,
- )
- }
- }
+ } else if (createOptionInfo.passwordCount != null) {
+ stringResource(
+ R.string.more_options_usage_passwords,
+ createOptionInfo.passwordCount
+ )
+ } else if (createOptionInfo.passkeyCount != null) {
+ stringResource(
+ R.string.more_options_usage_passkeys,
+ createOptionInfo.passkeyCount
+ )
+ } else {
+ null
}
- }
+ } else {
+ if (createOptionInfo.totalCredentialCount != null) {
+ stringResource(
+ R.string.more_options_usage_credentials,
+ createOptionInfo.totalCredentialCount
+ )
+ } else {
+ null
+ }
+ },
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MoreOptionsDisabledProvidersRow(
disabledProviders: List<ProviderInfo>?,
@@ -1033,36 +674,15 @@ fun MoreOptionsDisabledProvidersRow(
if (disabledProviders != null && disabledProviders.isNotEmpty()) {
Entry(
onClick = onDisabledProvidersSelected,
- icon = {
- Icon(
- Icons.Filled.Add,
- contentDescription = null,
- modifier = Modifier.padding(start = 16.dp),
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- )
+ iconImageVector = Icons.Filled.Add,
+ entryHeadlineText = stringResource(R.string.other_password_manager),
+ entrySecondLineText = disabledProviders.joinToString(separator = " • ") {
+ it.displayName
},
- label = {
- Column() {
- TextOnSurfaceVariant(
- text = stringResource(R.string.other_password_manager),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp),
- )
- // TODO: Update the subtitle once design is confirmed
- TextSecondary(
- text = disabledProviders.joinToString(separator = " • ") {
- it.displayName
- },
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
- )
- }
- }
)
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RemoteEntryRow(
remoteInfo: RemoteInfo,
@@ -1070,23 +690,7 @@ fun RemoteEntryRow(
) {
Entry(
onClick = { onRemoteEntrySelected(remoteInfo) },
- icon = {
- Icon(
- painter = painterResource(R.drawable.ic_other_devices),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- modifier = Modifier.padding(start = 10.dp)
- )
- },
- label = {
- Column() {
- TextOnSurfaceVariant(
- text = stringResource(R.string.another_device),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
- .align(alignment = Alignment.CenterHorizontally),
- )
- }
- }
+ iconPainter = painterResource(R.drawable.ic_other_devices),
+ entryHeadlineText = stringResource(R.string.another_device),
)
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index ce4e936446ff..192fa15714c6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -38,7 +38,9 @@ data class CreateCredentialUiState(
)
internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
- return state.sortedCreateOptionsPairs.isNotEmpty()
+ return state.sortedCreateOptionsPairs.isNotEmpty() ||
+ (!state.requestDisplayInfo.preferImmediatelyAvailableCredentials &&
+ state.remoteEntry != null)
}
open class ProviderInfo(
@@ -104,6 +106,7 @@ data class RequestDisplayInfo(
val type: CredentialType,
val appName: String,
val typeIcon: Drawable,
+ val preferImmediatelyAvailableCredentials: Boolean,
)
/**
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 5704820444a4..c5028c25c5da 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -20,40 +20,23 @@ import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
-
-import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material3.Divider
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Snackbar
-import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
-import androidx.compose.material3.TopAppBar
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
@@ -62,16 +45,18 @@ import com.android.credentialmanager.common.BaseEntry
import com.android.credentialmanager.common.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.ui.ActionButton
+import com.android.credentialmanager.common.ui.ActionEntry
import com.android.credentialmanager.common.ui.ConfirmButton
+import com.android.credentialmanager.common.ui.CredentialContainerCard
+import com.android.credentialmanager.common.ui.CtaButtonRow
import com.android.credentialmanager.common.ui.Entry
import com.android.credentialmanager.common.ui.ModalBottomSheet
-import com.android.credentialmanager.common.ui.TextOnSurface
-import com.android.credentialmanager.common.ui.TextSecondary
-import com.android.credentialmanager.common.ui.TextOnSurfaceVariant
-import com.android.credentialmanager.common.ui.ContainerCard
-import com.android.credentialmanager.common.ui.TransparentBackgroundEntry
-import com.android.credentialmanager.ui.theme.EntryShape
-import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
+import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
+import com.android.credentialmanager.common.ui.SheetContainerCard
+import com.android.credentialmanager.common.ui.SnackbarActionText
+import com.android.credentialmanager.common.ui.HeadlineText
+import com.android.credentialmanager.common.ui.CredentialListSectionHeader
+import com.android.credentialmanager.common.ui.Snackbar
@Composable
fun GetCredentialScreen(
@@ -154,12 +139,9 @@ fun PrimarySelectionCard(
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- ContainerCard() {
- Column() {
- TextOnSurface(
- modifier = Modifier.padding(all = 24.dp),
- textAlign = TextAlign.Center,
- style = MaterialTheme.typography.headlineSmall,
+ SheetContainerCard {
+ item {
+ HeadlineText(
text = stringResource(
if (sortedUserNameToCredentialEntryList
.size == 1 && authenticationEntryList.isEmpty()
@@ -178,48 +160,42 @@ fun PrimarySelectionCard(
requestDisplayInfo.appName
),
)
-
- ContainerCard(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier
- .padding(horizontal = 24.dp)
- .align(alignment = Alignment.CenterHorizontally)
- ) {
- val usernameForCredentialSize = sortedUserNameToCredentialEntryList
- .size
- val authenticationEntrySize = authenticationEntryList.size
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(2.dp)
- ) {
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item {
+ CredentialContainerCard {
+ Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+ val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size
+ val authenticationEntrySize = authenticationEntryList.size
// Show max 4 entries in this primary page
if (usernameForCredentialSize + authenticationEntrySize <= 4) {
- items(sortedUserNameToCredentialEntryList) {
+ sortedUserNameToCredentialEntryList.forEach {
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
)
}
- items(authenticationEntryList) {
+ authenticationEntryList.forEach {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
)
}
} else if (usernameForCredentialSize < 4) {
- items(sortedUserNameToCredentialEntryList) {
+ sortedUserNameToCredentialEntryList.forEach {
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
)
}
- items(authenticationEntryList.take(4 - usernameForCredentialSize)) {
+ authenticationEntryList.take(4 - usernameForCredentialSize).forEach {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
)
}
} else {
- items(sortedUserNameToCredentialEntryList.take(4)) {
+ sortedUserNameToCredentialEntryList.take(4).forEach {
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
@@ -228,49 +204,39 @@ fun PrimarySelectionCard(
}
}
}
- Divider(
- thickness = 24.dp,
- color = Color.Transparent
- )
- var totalEntriesCount = sortedUserNameToCredentialEntryList
- .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
- .size + providerInfoList.flatMap { it.actionEntryList }.size
- if (providerDisplayInfo.remoteEntry != null) totalEntriesCount += 1
- // Row horizontalArrangement differs on only one actionButton(should place on most
- // left)/only one confirmButton(should place on most right)/two buttons exist the same
- // time(should be one on the left, one on the right)
- Row(
- horizontalArrangement =
- if (totalEntriesCount <= 1 && activeEntry != null) Arrangement.End
- else if (totalEntriesCount > 1 && activeEntry == null) Arrangement.Start
- else Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
- ) {
- if (totalEntriesCount > 1) {
- ActionButton(
- stringResource(R.string.get_dialog_use_saved_passkey_for),
- onMoreOptionSelected
- )
- }
- // Only one sign-in options exist
- if (activeEntry != null) {
- ConfirmButton(
- stringResource(R.string.string_continue),
- onClick = onConfirm
- )
- }
- }
- Divider(
- thickness = 18.dp,
- color = Color.Transparent,
- modifier = Modifier.padding(bottom = 16.dp)
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ var totalEntriesCount = sortedUserNameToCredentialEntryList
+ .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList
+ .size + providerInfoList.flatMap { it.actionEntryList }.size
+ if (providerDisplayInfo.remoteEntry != null) totalEntriesCount += 1
+ // Row horizontalArrangement differs on only one actionButton(should place on most
+ // left)/only one confirmButton(should place on most right)/two buttons exist the same
+ // time(should be one on the left, one on the right)
+ item {
+ CtaButtonRow(
+ leftButton = if (totalEntriesCount > 1) {
+ {
+ ActionButton(
+ stringResource(R.string.get_dialog_use_saved_passkey_for),
+ onMoreOptionSelected
+ )
+ }
+ } else null,
+ rightButton = if (activeEntry != null) { // Only one sign-in options exist
+ {
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
+ )
+ }
+ } else null,
)
}
}
}
/** Draws the secondary credential selection page, where all sign-in options are listed. */
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
@@ -283,88 +249,52 @@ fun AllSignInOptionCard(
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- ContainerCard() {
- Column() {
- TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(
- containerColor = Color.Transparent,
- ),
- title = {
- TextOnSurface(
- text = stringResource(R.string.get_dialog_title_sign_in_options),
- style = MaterialTheme.typography.titleMedium
- )
- },
- navigationIcon = {
- IconButton(onClick = if (isNoAccount) onCancel else onBackButtonClicked) {
- Icon(
- Icons.Filled.ArrowBack,
- contentDescription = stringResource(
- R.string.accessibility_back_arrow_button)
- )
- }
- },
- modifier = Modifier.padding(top = 12.dp)
+ SheetContainerCard(topAppBar = {
+ MoreOptionTopAppBar(
+ text = stringResource(R.string.get_dialog_title_sign_in_options),
+ onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked,
+ )
+ }) {
+ // For username
+ items(sortedUserNameToCredentialEntryList) { item ->
+ PerUserNameCredentials(
+ perUserNameCredentialEntryList = item,
+ onEntrySelected = onEntrySelected,
)
-
- ContainerCard(
- shape = MaterialTheme.shapes.large,
- modifier = Modifier
- .padding(start = 24.dp, end = 24.dp, bottom = 24.dp)
- .align(alignment = Alignment.CenterHorizontally),
- ) {
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- // For username
- items(sortedUserNameToCredentialEntryList) { item ->
- PerUserNameCredentials(
- perUserNameCredentialEntryList = item,
- onEntrySelected = onEntrySelected,
- )
- }
- // Locked password manager
- if (authenticationEntryList.isNotEmpty()) {
- item {
- LockedCredentials(
- authenticationEntryList = authenticationEntryList,
- onEntrySelected = onEntrySelected,
- )
- }
- }
- item {
- Divider(
- thickness = 8.dp,
- color = Color.Transparent,
- )
- }
- // From another device
- val remoteEntry = providerDisplayInfo.remoteEntry
- if (remoteEntry != null) {
- item {
- RemoteEntryCard(
- remoteEntry = remoteEntry,
- onEntrySelected = onEntrySelected,
- )
- }
- }
- item {
- Divider(
- thickness = 1.dp,
- color = Color.LightGray,
- modifier = Modifier.padding(top = 16.dp)
- )
- }
- // Manage sign-ins (action chips)
- item {
- ActionChips(
- providerInfoList = providerInfoList,
- onEntrySelected = onEntrySelected
- )
- }
- }
+ }
+ // Locked password manager
+ if (authenticationEntryList.isNotEmpty()) {
+ item {
+ LockedCredentials(
+ authenticationEntryList = authenticationEntryList,
+ onEntrySelected = onEntrySelected,
+ )
}
}
+ // From another device
+ val remoteEntry = providerDisplayInfo.remoteEntry
+ if (remoteEntry != null) {
+ item {
+ RemoteEntryCard(
+ remoteEntry = remoteEntry,
+ onEntrySelected = onEntrySelected,
+ )
+ }
+ }
+ item {
+ Divider(
+ thickness = 1.dp,
+ color = Color.LightGray,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ }
+ // Manage sign-ins (action chips)
+ item {
+ ActionChips(
+ providerInfoList = providerInfoList,
+ onEntrySelected = onEntrySelected
+ )
+ }
}
}
@@ -381,17 +311,11 @@ fun ActionChips(
return
}
- TextSecondary(
- text = stringResource(R.string.get_dialog_heading_manage_sign_ins),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(vertical = 8.dp)
+ CredentialListSectionHeader(
+ text = stringResource(R.string.get_dialog_heading_manage_sign_ins)
)
- // TODO: tweak padding.
- ContainerCard(
- modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- shape = MaterialTheme.shapes.medium,
- ) {
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ CredentialContainerCard {
+ Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
actionChips.forEach {
ActionEntryRow(it, onEntrySelected)
}
@@ -404,38 +328,20 @@ fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
onEntrySelected: (BaseEntry) -> Unit,
) {
- TextSecondary(
- text = stringResource(R.string.get_dialog_heading_from_another_device),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(vertical = 8.dp)
+ CredentialListSectionHeader(
+ text = stringResource(R.string.get_dialog_heading_from_another_device)
)
- ContainerCard(
- modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- shape = MaterialTheme.shapes.medium,
- ) {
+ CredentialContainerCard {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Entry(
onClick = { onEntrySelected(remoteEntry) },
- icon = {
- Icon(
- painter = painterResource(R.drawable.ic_other_devices),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- modifier = Modifier.padding(start = 10.dp)
- )
- },
- label = {
- TextOnSurfaceVariant(
- text = stringResource(
- R.string.get_dialog_option_headline_use_a_different_device),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
- .align(alignment = Alignment.CenterHorizontally)
- )
- }
+ iconPainter = painterResource(R.drawable.ic_other_devices),
+ entryHeadlineText = stringResource(
+ R.string.get_dialog_option_headline_use_a_different_device
+ ),
)
}
}
@@ -446,15 +352,10 @@ fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
onEntrySelected: (BaseEntry) -> Unit,
) {
- TextSecondary(
- text = stringResource(R.string.get_dialog_heading_locked_password_managers),
- style = MaterialTheme.typography.labelLarge,
- modifier = Modifier.padding(vertical = 8.dp)
+ CredentialListSectionHeader(
+ text = stringResource(R.string.get_dialog_heading_locked_password_managers)
)
- ContainerCard(
- modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- shape = MaterialTheme.shapes.medium,
- ) {
+ CredentialContainerCard {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
@@ -471,17 +372,12 @@ fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
onEntrySelected: (BaseEntry) -> Unit,
) {
- TextSecondary(
+ CredentialListSectionHeader(
text = stringResource(
R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName
- ),
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(vertical = 8.dp)
+ )
)
- ContainerCard(
- modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- shape = MaterialTheme.shapes.medium,
- ) {
+ CredentialContainerCard {
Column(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
@@ -500,53 +396,28 @@ fun CredentialEntryRow(
) {
Entry(
onClick = { onEntrySelected(credentialEntryInfo) },
- icon = {
- if (credentialEntryInfo.icon != null) {
- Image(
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
- contentDescription = null,
- )
- } else {
- Icon(
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- painter = painterResource(R.drawable.ic_other_sign_in),
- contentDescription = null,
- tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant
- )
- }
+ iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(),
+ // Fall back to iconPainter if iconImageBitmap isn't available
+ iconPainter =
+ if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in)
+ else null,
+ entryHeadlineText = credentialEntryInfo.userName,
+ entrySecondLineText = if (
+ credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
+ "••••••••••••"
+ } else {
+ if (TextUtils.isEmpty(credentialEntryInfo.displayName))
+ credentialEntryInfo.credentialTypeDisplayName
+ else
+ credentialEntryInfo.credentialTypeDisplayName +
+ stringResource(
+ R.string.get_dialog_sign_in_type_username_separator
+ ) +
+ credentialEntryInfo.displayName
},
- label = {
- Column() {
- // TODO: fix the text values.
- TextOnSurfaceVariant(
- text = credentialEntryInfo.userName,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp, start = 5.dp)
- )
- TextSecondary(
- text = if (
- credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
- "••••••••••••"
- } else {
- if (TextUtils.isEmpty(credentialEntryInfo.displayName))
- credentialEntryInfo.credentialTypeDisplayName
- else
- credentialEntryInfo.credentialTypeDisplayName +
- stringResource(
- R.string.get_dialog_sign_in_type_username_separator
- ) +
- credentialEntryInfo.displayName
- },
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp, start = 5.dp)
- )
- }
- }
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
@@ -554,144 +425,66 @@ fun AuthenticationEntryRow(
) {
Entry(
onClick = { onEntrySelected(authenticationEntryInfo) },
- icon = {
- Image(
- modifier = Modifier.padding(start = 10.dp).size(32.dp),
- bitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
- contentDescription = null
- )
- },
- label = {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp),
- ) {
- Column() {
- TextOnSurfaceVariant(
- text = authenticationEntryInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(top = 16.dp)
- )
- TextSecondary(
- text = stringResource(
- if (authenticationEntryInfo.isUnlockedAndEmpty)
- R.string.locked_credential_entry_label_subtext_no_sign_in
- else R.string.locked_credential_entry_label_subtext_tap_to_unlock
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(bottom = 16.dp)
- )
- }
- if (!authenticationEntryInfo.isUnlockedAndEmpty) {
- Icon(
- Icons.Outlined.Lock,
- null,
- Modifier.align(alignment = Alignment.CenterVertically).padding(end = 10.dp),
- )
- }
- }
- }
+ iconImageBitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
+ entryHeadlineText = authenticationEntryInfo.title,
+ entrySecondLineText = stringResource(
+ if (authenticationEntryInfo.isUnlockedAndEmpty)
+ R.string.locked_credential_entry_label_subtext_no_sign_in
+ else R.string.locked_credential_entry_label_subtext_tap_to_unlock
+ ),
+ isLockedAuthEntry = !authenticationEntryInfo.isUnlockedAndEmpty,
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ActionEntryRow(
actionEntryInfo: ActionEntryInfo,
onEntrySelected: (BaseEntry) -> Unit,
) {
- TransparentBackgroundEntry(
- icon = {
- Image(
- modifier = Modifier.padding(start = 10.dp).size(24.dp),
- bitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
- contentDescription = null,
- )
- },
- label = {
- Column() {
- TextOnSurfaceVariant(
- text = actionEntryInfo.title,
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 8.dp),
- )
- if (actionEntryInfo.subTitle != null) {
- TextSecondary(
- text = actionEntryInfo.subTitle,
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(start = 8.dp),
- )
- }
- }
- },
+ ActionEntry(
+ iconImageBitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
+ entryHeadlineText = actionEntryInfo.title,
+ entrySecondLineText = actionEntryInfo.subTitle,
onClick = { onEntrySelected(actionEntryInfo) },
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RemoteCredentialSnackBarScreen(
onClick: (Boolean) -> Unit,
onCancel: () -> Unit,
) {
- // TODO: Change the height, width and position according to the design
Snackbar(
- modifier = Modifier.padding(horizontal = 40.dp).padding(top = 700.dp),
- shape = EntryShape.FullMediumRoundedCorner,
- containerColor = LocalAndroidColorScheme.current.colorBackground,
- contentColor = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
action = {
TextButton(
+ modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp)
+ .heightIn(min = 32.dp),
onClick = { onClick(true) },
+ contentPadding =
+ PaddingValues(start = 0.dp, top = 6.dp, end = 0.dp, bottom = 6.dp),
) {
- Text(text = stringResource(R.string.snackbar_action))
+ SnackbarActionText(text = stringResource(R.string.snackbar_action))
}
},
- dismissAction = {
- IconButton(onClick = onCancel) {
- Icon(
- Icons.Filled.Close,
- contentDescription = stringResource(
- R.string.accessibility_close_button
- ),
- tint = LocalAndroidColorScheme.current.colorAccentTertiary
- )
- }
- },
- ) {
- Text(text = stringResource(R.string.get_dialog_use_saved_passkey_for))
- }
+ onDismiss = onCancel,
+ contentText = stringResource(R.string.get_dialog_use_saved_passkey_for),
+ )
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EmptyAuthEntrySnackBarScreen(
authenticationEntryList: List<AuthenticationEntryInfo>,
onCancel: () -> Unit,
onLastLockedAuthEntryNotFound: () -> Unit,
) {
- val lastLocked = authenticationEntryList.firstOrNull({it.isLastUnlocked})
+ val lastLocked = authenticationEntryList.firstOrNull({ it.isLastUnlocked })
if (lastLocked == null) {
onLastLockedAuthEntryNotFound()
return
}
- // TODO: Change the height, width and position according to the design
Snackbar(
- modifier = Modifier.padding(horizontal = 40.dp).padding(top = 700.dp),
- shape = EntryShape.FullMediumRoundedCorner,
- containerColor = LocalAndroidColorScheme.current.colorBackground,
- contentColor = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
- dismissAction = {
- IconButton(onClick = onCancel) {
- Icon(
- Icons.Filled.Close,
- contentDescription = stringResource(R.string.accessibility_close_button),
- tint = LocalAndroidColorScheme.current.colorAccentTertiary
- )
- }
- },
- ) {
- Text(text = stringResource(R.string.no_sign_in_info_in, lastLocked.title))
- }
+ onDismiss = onCancel,
+ contentText = stringResource(R.string.no_sign_in_info_in, lastLocked.providerDisplayName),
+ )
} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 49415c01033d..9727d3f39c4a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -37,7 +37,8 @@ data class GetCredentialUiState(
internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean {
return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() ||
state.providerDisplayInfo.authenticationEntryList.isNotEmpty() ||
- state.providerDisplayInfo.remoteEntry != null
+ (state.providerDisplayInfo.remoteEntry != null &&
+ !state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
}
data class ProviderInfo(
@@ -96,6 +97,7 @@ class AuthenticationEntryInfo(
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
val title: String,
+ val providerDisplayName: String,
val icon: Drawable,
// The entry had been unlocked and turned out to be empty. Used to determine whether to
// show "Tap to unlock" or "No sign-in info" for this entry.
@@ -140,11 +142,12 @@ class ActionEntryInfo(
entrySubkey,
pendingIntent,
fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = false,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
data class RequestDisplayInfo(
val appName: String,
+ val preferImmediatelyAvailableCredentials: Boolean,
)
/**
@@ -245,7 +248,6 @@ private fun toActiveEntry(
private fun toGetScreenState(
providerDisplayInfo: ProviderDisplayInfo
): GetScreenState {
-
return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
providerDisplayInfo.remoteEntry == null &&
providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty })
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
index 15ae3295416b..120e4938c322 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
@@ -22,12 +22,14 @@ import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import com.android.internal.R
+/** File copied from PlatformComposeCore. */
+
/** CompositionLocal used to pass [AndroidColorScheme] down the tree. */
val LocalAndroidColorScheme =
staticCompositionLocalOf<AndroidColorScheme> {
throw IllegalStateException(
"No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " +
- "Composable surrounded by a CredentialSelectorTheme {}."
+ "Composable surrounded by a PlatformTheme {}."
)
}
@@ -38,7 +40,6 @@ val LocalAndroidColorScheme =
* most of the colors in this class will be removed in favor of their M3 counterpart.
*/
class AndroidColorScheme internal constructor(context: Context) {
-
val colorPrimary = getColor(context, R.attr.colorPrimary)
val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark)
val colorAccent = getColor(context, R.attr.colorAccent)
@@ -66,10 +67,12 @@ class AndroidColorScheme internal constructor(context: Context) {
val colorForeground = getColor(context, R.attr.colorForeground)
val colorForegroundInverse = getColor(context, R.attr.colorForegroundInverse)
- private fun getColor(context: Context, attr: Int): Color {
- val ta = context.obtainStyledAttributes(intArrayOf(attr))
- @ColorInt val color = ta.getColor(0, 0)
- ta.recycle()
- return Color(color)
+ companion object {
+ fun getColor(context: Context, attr: Int): Color {
+ val ta = context.obtainStyledAttributes(intArrayOf(attr))
+ @ColorInt val color = ta.getColor(0, 0)
+ ta.recycle()
+ return Color(color)
+ }
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
index abb4bfbf915e..c9238459633c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
@@ -1,14 +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 com.android.credentialmanager.ui.theme
+import android.annotation.AttrRes
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
-val Grey100 = Color(0xFFF1F3F4)
-val Purple200 = Color(0xFFBB86FC)
-val Purple500 = Color(0xFF6200EE)
-val Purple700 = Color(0xFF3700B3)
-val Teal200 = Color(0xFF03DAC5)
-val lightColorAccentSecondary = Color(0xFFC2E7FF)
-val lightBackgroundColor = Color(0xFFF0F0F0)
-val lightSurface1 = Color(0xFF6991D6)
-val textColorSecondary = Color(0xFF40484B)
-val textColorPrimary = Color(0xFF191C1D)
+/** Read the [Color] from the given [attribute]. */
+@Composable
+@ReadOnlyComposable
+fun colorAttr(@AttrRes attribute: Int): Color {
+ return AndroidColorScheme.getColor(LocalContext.current, attribute)
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt
new file mode 100644
index 000000000000..662199a4bba5
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/PlatformTheme.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.credentialmanager.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import com.android.credentialmanager.ui.theme.typography.TypeScaleTokens
+import com.android.credentialmanager.ui.theme.typography.TypefaceNames
+import com.android.credentialmanager.ui.theme.typography.TypefaceTokens
+import com.android.credentialmanager.ui.theme.typography.TypographyTokens
+import com.android.credentialmanager.ui.theme.typography.platformTypography
+
+/** File copied from PlatformComposeCore. */
+
+/** The Material 3 theme that should wrap all Platform Composables. */
+@Composable
+fun PlatformTheme(
+ isDarkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit,
+) {
+ val context = LocalContext.current
+
+ // TODO(b/230605885): Define our color scheme.
+ val colorScheme =
+ if (isDarkTheme) {
+ dynamicDarkColorScheme(context)
+ } else {
+ dynamicLightColorScheme(context)
+ }
+ val androidColorScheme = AndroidColorScheme(context)
+ val typefaceNames = remember(context) { TypefaceNames.get(context) }
+ val typography =
+ remember(typefaceNames) {
+ platformTypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
+ }
+
+ MaterialTheme(colorScheme, typography = typography) {
+ CompositionLocalProvider(
+ LocalAndroidColorScheme provides androidColorScheme,
+ ) {
+ content()
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
deleted file mode 100644
index 3ca0e4494ab6..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.android.credentialmanager.ui.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.platform.LocalContext
-
-@Composable
-fun CredentialSelectorTheme(
- darkTheme: Boolean = isSystemInDarkTheme(),
- content: @Composable () -> Unit
-) {
- val context = LocalContext.current
-
- val colorScheme =
- if (darkTheme) {
- dynamicDarkColorScheme(context)
- } else {
- dynamicLightColorScheme(context)
- }
- val androidColorScheme = AndroidColorScheme(context)
- val typography = Typography
-
- MaterialTheme(
- colorScheme,
- typography = typography,
- shapes = Shapes
- ) {
- CompositionLocalProvider(
- LocalAndroidColorScheme provides androidColorScheme,
- ) {
- content()
- }
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
deleted file mode 100644
index e09abbb3ffff..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.android.credentialmanager.ui.theme
-
-import androidx.compose.material3.Typography
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
-
-// Set of Material typography styles to start with
-val Typography = Typography(
- titleMedium = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 24.sp,
- lineHeight = 32.sp,
- ),
- bodyLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 14.sp,
- lineHeight = 20.sp,
- ),
- bodyMedium = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 14.sp,
- lineHeight = 20.sp,
- color = textColorSecondary
- ),
- labelLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Medium,
- fontSize = 14.sp,
- lineHeight = 20.sp,
- ),
- titleLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Medium,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- color = textColorPrimary
- ),
-
- /* Other default text styles to override
- button = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.W500,
- fontSize = 14.sp
- ),
- caption = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 12.sp
- )
- */
-)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt
new file mode 100644
index 000000000000..984e4f19e4d4
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/PlatformTypography.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.credentialmanager.ui.theme.typography
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Typography
+
+/** File copied from PlatformComposeCore. */
+
+/**
+ * The typography for Platform Compose code.
+ *
+ * Do not use directly and call [MaterialTheme.typography] instead to access the different text
+ * styles.
+ */
+internal fun platformTypography(typographyTokens: TypographyTokens): Typography {
+ return Typography(
+ displayLarge = typographyTokens.displayLarge,
+ displayMedium = typographyTokens.displayMedium,
+ displaySmall = typographyTokens.displaySmall,
+ headlineLarge = typographyTokens.headlineLarge,
+ headlineMedium = typographyTokens.headlineMedium,
+ headlineSmall = typographyTokens.headlineSmall,
+ titleLarge = typographyTokens.titleLarge,
+ titleMedium = typographyTokens.titleMedium,
+ titleSmall = typographyTokens.titleSmall,
+ bodyLarge = typographyTokens.bodyLarge,
+ bodyMedium = typographyTokens.bodyMedium,
+ bodySmall = typographyTokens.bodySmall,
+ labelLarge = typographyTokens.labelLarge,
+ labelMedium = typographyTokens.labelMedium,
+ labelSmall = typographyTokens.labelSmall,
+ )
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt
new file mode 100644
index 000000000000..b2dd20720f6a
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypeScaleTokens.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.credentialmanager.ui.theme.typography
+
+import androidx.compose.ui.unit.sp
+
+/** File copied from PlatformComposeCore. */
+internal class TypeScaleTokens(typefaceTokens: TypefaceTokens) {
+ val bodyLargeFont = typefaceTokens.plain
+ val bodyLargeLineHeight = 24.0.sp
+ val bodyLargeSize = 16.sp
+ val bodyLargeTracking = 0.0.sp
+ val bodyLargeWeight = TypefaceTokens.WeightRegular
+ val bodyMediumFont = typefaceTokens.plain
+ val bodyMediumLineHeight = 20.0.sp
+ val bodyMediumSize = 14.sp
+ val bodyMediumTracking = 0.0.sp
+ val bodyMediumWeight = TypefaceTokens.WeightRegular
+ val bodySmallFont = typefaceTokens.plain
+ val bodySmallLineHeight = 16.0.sp
+ val bodySmallSize = 12.sp
+ val bodySmallTracking = 0.1.sp
+ val bodySmallWeight = TypefaceTokens.WeightRegular
+ val displayLargeFont = typefaceTokens.brand
+ val displayLargeLineHeight = 64.0.sp
+ val displayLargeSize = 57.sp
+ val displayLargeTracking = 0.0.sp
+ val displayLargeWeight = TypefaceTokens.WeightRegular
+ val displayMediumFont = typefaceTokens.brand
+ val displayMediumLineHeight = 52.0.sp
+ val displayMediumSize = 45.sp
+ val displayMediumTracking = 0.0.sp
+ val displayMediumWeight = TypefaceTokens.WeightRegular
+ val displaySmallFont = typefaceTokens.brand
+ val displaySmallLineHeight = 44.0.sp
+ val displaySmallSize = 36.sp
+ val displaySmallTracking = 0.0.sp
+ val displaySmallWeight = TypefaceTokens.WeightRegular
+ val headlineLargeFont = typefaceTokens.brand
+ val headlineLargeLineHeight = 40.0.sp
+ val headlineLargeSize = 32.sp
+ val headlineLargeTracking = 0.0.sp
+ val headlineLargeWeight = TypefaceTokens.WeightRegular
+ val headlineMediumFont = typefaceTokens.brand
+ val headlineMediumLineHeight = 36.0.sp
+ val headlineMediumSize = 28.sp
+ val headlineMediumTracking = 0.0.sp
+ val headlineMediumWeight = TypefaceTokens.WeightRegular
+ val headlineSmallFont = typefaceTokens.brand
+ val headlineSmallLineHeight = 32.0.sp
+ val headlineSmallSize = 24.sp
+ val headlineSmallTracking = 0.0.sp
+ val headlineSmallWeight = TypefaceTokens.WeightRegular
+ val labelLargeFont = typefaceTokens.plain
+ val labelLargeLineHeight = 20.0.sp
+ val labelLargeSize = 14.sp
+ val labelLargeTracking = 0.0.sp
+ val labelLargeWeight = TypefaceTokens.WeightMedium
+ val labelMediumFont = typefaceTokens.plain
+ val labelMediumLineHeight = 16.0.sp
+ val labelMediumSize = 12.sp
+ val labelMediumTracking = 0.1.sp
+ val labelMediumWeight = TypefaceTokens.WeightMedium
+ val labelSmallFont = typefaceTokens.plain
+ val labelSmallLineHeight = 16.0.sp
+ val labelSmallSize = 11.sp
+ val labelSmallTracking = 0.1.sp
+ val labelSmallWeight = TypefaceTokens.WeightMedium
+ val titleLargeFont = typefaceTokens.brand
+ val titleLargeLineHeight = 28.0.sp
+ val titleLargeSize = 22.sp
+ val titleLargeTracking = 0.0.sp
+ val titleLargeWeight = TypefaceTokens.WeightRegular
+ val titleMediumFont = typefaceTokens.plain
+ val titleMediumLineHeight = 24.0.sp
+ val titleMediumSize = 16.sp
+ val titleMediumTracking = 0.0.sp
+ val titleMediumWeight = TypefaceTokens.WeightMedium
+ val titleSmallFont = typefaceTokens.plain
+ val titleSmallLineHeight = 20.0.sp
+ val titleSmallSize = 14.sp
+ val titleSmallTracking = 0.0.sp
+ val titleSmallWeight = TypefaceTokens.WeightMedium
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.kt
new file mode 100644
index 000000000000..3cc761f1cc60
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypefaceTokens.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.
+ */
+
+@file:OptIn(ExperimentalTextApi::class)
+
+package com.android.credentialmanager.ui.theme.typography
+
+import android.content.Context
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.font.DeviceFontFamilyName
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+
+/** File copied from PlatformComposeCore. */
+internal class TypefaceTokens(typefaceNames: TypefaceNames) {
+ companion object {
+ val WeightMedium = FontWeight.Medium
+ val WeightRegular = FontWeight.Normal
+ }
+
+ private val brandFont = DeviceFontFamilyName(typefaceNames.brand)
+ private val plainFont = DeviceFontFamilyName(typefaceNames.plain)
+
+ val brand =
+ FontFamily(
+ Font(brandFont, weight = WeightMedium),
+ Font(brandFont, weight = WeightRegular),
+ )
+ val plain =
+ FontFamily(
+ Font(plainFont, weight = WeightMedium),
+ Font(plainFont, weight = WeightRegular),
+ )
+}
+
+internal data class TypefaceNames
+private constructor(
+ val brand: String,
+ val plain: String,
+) {
+ private enum class Config(val configName: String, val default: String) {
+ Brand("config_headlineFontFamily", "sans-serif"),
+ Plain("config_bodyFontFamily", "sans-serif"),
+ }
+
+ companion object {
+ fun get(context: Context): TypefaceNames {
+ return TypefaceNames(
+ brand = getTypefaceName(context, Config.Brand),
+ plain = getTypefaceName(context, Config.Plain),
+ )
+ }
+
+ private fun getTypefaceName(context: Context, config: Config): String {
+ return context
+ .getString(context.resources.getIdentifier(config.configName, "string", "android"))
+ .takeIf { it.isNotEmpty() }
+ ?: config.default
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt
new file mode 100644
index 000000000000..aadab92f40cc
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/typography/TypographyTokens.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.credentialmanager.ui.theme.typography
+
+import androidx.compose.ui.text.TextStyle
+
+/** File copied from PlatformComposeCore. */
+internal class TypographyTokens(typeScaleTokens: TypeScaleTokens) {
+ val bodyLarge =
+ TextStyle(
+ fontFamily = typeScaleTokens.bodyLargeFont,
+ fontWeight = typeScaleTokens.bodyLargeWeight,
+ fontSize = typeScaleTokens.bodyLargeSize,
+ lineHeight = typeScaleTokens.bodyLargeLineHeight,
+ letterSpacing = typeScaleTokens.bodyLargeTracking,
+ )
+ val bodyMedium =
+ TextStyle(
+ fontFamily = typeScaleTokens.bodyMediumFont,
+ fontWeight = typeScaleTokens.bodyMediumWeight,
+ fontSize = typeScaleTokens.bodyMediumSize,
+ lineHeight = typeScaleTokens.bodyMediumLineHeight,
+ letterSpacing = typeScaleTokens.bodyMediumTracking,
+ )
+ val bodySmall =
+ TextStyle(
+ fontFamily = typeScaleTokens.bodySmallFont,
+ fontWeight = typeScaleTokens.bodySmallWeight,
+ fontSize = typeScaleTokens.bodySmallSize,
+ lineHeight = typeScaleTokens.bodySmallLineHeight,
+ letterSpacing = typeScaleTokens.bodySmallTracking,
+ )
+ val displayLarge =
+ TextStyle(
+ fontFamily = typeScaleTokens.displayLargeFont,
+ fontWeight = typeScaleTokens.displayLargeWeight,
+ fontSize = typeScaleTokens.displayLargeSize,
+ lineHeight = typeScaleTokens.displayLargeLineHeight,
+ letterSpacing = typeScaleTokens.displayLargeTracking,
+ )
+ val displayMedium =
+ TextStyle(
+ fontFamily = typeScaleTokens.displayMediumFont,
+ fontWeight = typeScaleTokens.displayMediumWeight,
+ fontSize = typeScaleTokens.displayMediumSize,
+ lineHeight = typeScaleTokens.displayMediumLineHeight,
+ letterSpacing = typeScaleTokens.displayMediumTracking,
+ )
+ val displaySmall =
+ TextStyle(
+ fontFamily = typeScaleTokens.displaySmallFont,
+ fontWeight = typeScaleTokens.displaySmallWeight,
+ fontSize = typeScaleTokens.displaySmallSize,
+ lineHeight = typeScaleTokens.displaySmallLineHeight,
+ letterSpacing = typeScaleTokens.displaySmallTracking,
+ )
+ val headlineLarge =
+ TextStyle(
+ fontFamily = typeScaleTokens.headlineLargeFont,
+ fontWeight = typeScaleTokens.headlineLargeWeight,
+ fontSize = typeScaleTokens.headlineLargeSize,
+ lineHeight = typeScaleTokens.headlineLargeLineHeight,
+ letterSpacing = typeScaleTokens.headlineLargeTracking,
+ )
+ val headlineMedium =
+ TextStyle(
+ fontFamily = typeScaleTokens.headlineMediumFont,
+ fontWeight = typeScaleTokens.headlineMediumWeight,
+ fontSize = typeScaleTokens.headlineMediumSize,
+ lineHeight = typeScaleTokens.headlineMediumLineHeight,
+ letterSpacing = typeScaleTokens.headlineMediumTracking,
+ )
+ val headlineSmall =
+ TextStyle(
+ fontFamily = typeScaleTokens.headlineSmallFont,
+ fontWeight = typeScaleTokens.headlineSmallWeight,
+ fontSize = typeScaleTokens.headlineSmallSize,
+ lineHeight = typeScaleTokens.headlineSmallLineHeight,
+ letterSpacing = typeScaleTokens.headlineSmallTracking,
+ )
+ val labelLarge =
+ TextStyle(
+ fontFamily = typeScaleTokens.labelLargeFont,
+ fontWeight = typeScaleTokens.labelLargeWeight,
+ fontSize = typeScaleTokens.labelLargeSize,
+ lineHeight = typeScaleTokens.labelLargeLineHeight,
+ letterSpacing = typeScaleTokens.labelLargeTracking,
+ )
+ val labelMedium =
+ TextStyle(
+ fontFamily = typeScaleTokens.labelMediumFont,
+ fontWeight = typeScaleTokens.labelMediumWeight,
+ fontSize = typeScaleTokens.labelMediumSize,
+ lineHeight = typeScaleTokens.labelMediumLineHeight,
+ letterSpacing = typeScaleTokens.labelMediumTracking,
+ )
+ val labelSmall =
+ TextStyle(
+ fontFamily = typeScaleTokens.labelSmallFont,
+ fontWeight = typeScaleTokens.labelSmallWeight,
+ fontSize = typeScaleTokens.labelSmallSize,
+ lineHeight = typeScaleTokens.labelSmallLineHeight,
+ letterSpacing = typeScaleTokens.labelSmallTracking,
+ )
+ val titleLarge =
+ TextStyle(
+ fontFamily = typeScaleTokens.titleLargeFont,
+ fontWeight = typeScaleTokens.titleLargeWeight,
+ fontSize = typeScaleTokens.titleLargeSize,
+ lineHeight = typeScaleTokens.titleLargeLineHeight,
+ letterSpacing = typeScaleTokens.titleLargeTracking,
+ )
+ val titleMedium =
+ TextStyle(
+ fontFamily = typeScaleTokens.titleMediumFont,
+ fontWeight = typeScaleTokens.titleMediumWeight,
+ fontSize = typeScaleTokens.titleMediumSize,
+ lineHeight = typeScaleTokens.titleMediumLineHeight,
+ letterSpacing = typeScaleTokens.titleMediumTracking,
+ )
+ val titleSmall =
+ TextStyle(
+ fontFamily = typeScaleTokens.titleSmallFont,
+ fontWeight = typeScaleTokens.titleSmallWeight,
+ fontSize = typeScaleTokens.titleSmallSize,
+ lineHeight = typeScaleTokens.titleSmallLineHeight,
+ letterSpacing = typeScaleTokens.titleSmallTracking,
+ )
+}
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index b194738c67b6..c2aaeace1af6 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -21,12 +21,7 @@
android:exported="true"
android:permission="android.permission.INSTALL_DYNAMIC_SYSTEM"
android:foregroundServiceType="systemExempted"
- android:process=":dynsystem">
- <intent-filter>
- <action android:name="android.os.image.action.NOTIFY_IF_IN_USE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </service>
+ android:process=":dynsystem" />
<activity android:name=".VerificationActivity"
android:exported="true"
diff --git a/packages/SettingsLib/DeviceStateRotationLock/OWNERS b/packages/SettingsLib/DeviceStateRotationLock/OWNERS
new file mode 100644
index 000000000000..091df2610866
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
index 724588f794b4..3fdb1d14e667 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -77,9 +77,8 @@ data class SettingsPage(
return true
}
- fun isEnabled(): Boolean {
- return getPageProvider(sppName)?.isEnabled(arguments) ?: false
- }
+ fun isEnabled(): Boolean =
+ SpaEnvironment.IS_DEBUG || getPageProvider(sppName)?.isEnabled(arguments) ?: false
fun getTitle(): String {
return getPageProvider(sppName)?.getTitle(arguments) ?: ""
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 02962a5815a2..2d956d5eddb2 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -88,4 +88,13 @@ abstract class SpaEnvironment(context: Context) {
open val sliceProviderAuthorities: String? = null
// TODO: add other environment setup here.
+ companion object {
+ /**
+ * Whether debug mode is on or off.
+ *
+ * If set to true, this will also enable all the pages under development (allows browsing
+ * and searching).
+ */
+ const val IS_DEBUG = false
+ }
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 5e6c61423c24..0552c408a8eb 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -46,6 +45,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -100,11 +100,16 @@ private fun RowScope.ActionButton(actionButton: ActionButton) {
contentDescription = null,
modifier = Modifier.size(SettingsDimension.itemIconSize),
)
- Spacer(Modifier.height(4.dp))
- Text(
- text = actionButton.text,
- style = MaterialTheme.typography.labelMedium,
- )
+ Box(
+ modifier = Modifier.padding(top = 4.dp).fillMaxHeight(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = actionButton.text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium,
+ )
+ }
}
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
index 18b207337ad4..1a7d8968f232 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
@@ -19,7 +19,6 @@ package com.android.settingslib.spaprivileged.model.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.drawable.Drawable
-import android.os.UserManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
@@ -28,6 +27,7 @@ import androidx.compose.ui.res.stringResource
import com.android.settingslib.Utils
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -42,23 +42,25 @@ interface AppRepository {
val context = LocalContext.current
return produceState(initialValue = stringResource(R.string.summary_placeholder), app) {
withContext(Dispatchers.IO) {
- if (isClonedAppPage || isCloneApp(context, app)) {
- value = context.getString(R.string.cloned_app_info_label, loadLabel(app))
+ value = if (isClonedAppPage || isCloneApp(context, app)) {
+ context.getString(R.string.cloned_app_info_label, loadLabel(app))
} else {
- value = loadLabel(app)
+ loadLabel(app)
}
}
}
}
private fun isCloneApp(context: Context, app: ApplicationInfo): Boolean {
- val userManager = context.getSystemService(UserManager::class.java)!!
- val userInfo = userManager.getUserInfo(app.userId)
+ val userInfo = context.userManager.getUserInfo(app.userId)
return userInfo != null && userInfo.isCloneProfile
}
@Composable
fun produceIcon(app: ApplicationInfo): State<Drawable?>
+
+ @Composable
+ fun produceIconContentDescription(app: ApplicationInfo): State<String?>
}
internal class AppRepositoryImpl(private val context: Context) : AppRepository {
@@ -69,8 +71,22 @@ internal class AppRepositoryImpl(private val context: Context) : AppRepository {
@Composable
override fun produceIcon(app: ApplicationInfo) =
produceState<Drawable?>(initialValue = null, app) {
- withContext(Dispatchers.Default) {
+ withContext(Dispatchers.IO) {
value = Utils.getBadgedIcon(context, app)
}
}
+
+ @Composable
+ override fun produceIconContentDescription(app: ApplicationInfo) =
+ produceState<String?>(initialValue = null, app) {
+ withContext(Dispatchers.IO) {
+ value = when {
+ context.userManager.isManagedProfile(app.userId) -> {
+ context.getString(R.string.category_work)
+ }
+
+ else -> null
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index 602df54ed3fb..e3ea2e78756f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -32,6 +32,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
@@ -50,7 +51,8 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
.padding(
horizontal = SettingsDimension.itemPaddingStart,
vertical = SettingsDimension.itemPaddingVertical,
- ),
+ )
+ .semantics(mergeDescendants = true) {},
horizontalAlignment = Alignment.CenterHorizontally,
) {
val app = packageInfo.applicationInfo
@@ -93,8 +95,8 @@ internal fun AppIcon(app: ApplicationInfo, size: Dp) {
val appRepository = rememberAppRepository()
Image(
painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
- contentDescription = null,
- modifier = Modifier.size(size)
+ contentDescription = appRepository.produceIconContentDescription(app).value,
+ modifier = Modifier.size(size),
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
index fc40aed43c92..4f0cddef078b 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
@@ -103,6 +103,9 @@ class AppListViewModelTest {
@Composable
override fun produceIcon(app: ApplicationInfo) = stateOf(null)
+
+ @Composable
+ override fun produceIconContentDescription(app: ApplicationInfo) = stateOf(null)
}
private companion object {
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt
new file mode 100644
index 000000000000..26caa01192c5
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppRepositoryTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.UserManager
+import androidx.compose.runtime.State
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppRepositoryTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @get:Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Spy
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock
+ private lateinit var userManager: UserManager
+
+ private lateinit var appRepository: AppRepositoryImpl
+
+ @Before
+ fun setUp() {
+ whenever(context.userManager).thenReturn(userManager)
+ appRepository = AppRepositoryImpl(context)
+ }
+
+ @Test
+ fun produceIconContentDescription_workProfile() {
+ whenever(userManager.isManagedProfile(APP.userId)).thenReturn(true)
+
+ val contentDescription = produceIconContentDescription()
+
+ assertThat(contentDescription.value).isEqualTo(context.getString(R.string.category_work))
+ }
+
+ @Test
+ fun produceIconContentDescription_personalProfile() {
+ whenever(userManager.isManagedProfile(APP.userId)).thenReturn(false)
+
+ val contentDescription = produceIconContentDescription()
+
+ assertThat(contentDescription.value).isNull()
+ }
+
+ private fun produceIconContentDescription(): State<String?> {
+ var contentDescription: State<String?> = stateOf(null)
+ composeTestRule.setContent {
+ contentDescription = appRepository.produceIconContentDescription(APP)
+ }
+ composeTestRule.delay()
+ return contentDescription
+ }
+
+ private companion object {
+ const val UID = 123
+ val APP = ApplicationInfo().apply {
+ uid = UID
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 888b09fafd3b..e846480f68d9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -575,9 +575,15 @@ public class Utils {
/** Get the corresponding adaptive icon drawable. */
public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) {
+ UserManager um = context.getSystemService(UserManager.class);
+ boolean isClone = um.getProfiles(user.getIdentifier()).stream()
+ .anyMatch(profile ->
+ profile.isCloneProfile() && profile.id == user.getIdentifier());
try (IconFactory iconFactory = IconFactory.obtain(context)) {
return iconFactory
- .createBadgedIconBitmap(icon, new IconOptions().setUser(user))
+ .createBadgedIconBitmap(
+ icon,
+ new IconOptions().setUser(user).setIsCloneProfile(isClone))
.newIcon(context);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d7c7130bfb3d..ba275ebca168 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -422,22 +422,26 @@ public class SettingsProvider extends ContentProvider {
switch (method) {
case Settings.CALL_METHOD_GET_CONFIG: {
Setting setting = getConfigSetting(name);
- return packageValueForCallResult(setting, isTrackingGeneration(args));
+ return packageValueForCallResult(SETTINGS_TYPE_CONFIG, name, requestingUserId,
+ setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_GET_GLOBAL: {
Setting setting = getGlobalSetting(name);
- return packageValueForCallResult(setting, isTrackingGeneration(args));
+ return packageValueForCallResult(SETTINGS_TYPE_GLOBAL, name, requestingUserId,
+ setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_GET_SECURE: {
Setting setting = getSecureSetting(name, requestingUserId);
- return packageValueForCallResult(setting, isTrackingGeneration(args));
+ return packageValueForCallResult(SETTINGS_TYPE_SECURE, name, requestingUserId,
+ setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_GET_SYSTEM: {
Setting setting = getSystemSetting(name, requestingUserId);
- return packageValueForCallResult(setting, isTrackingGeneration(args));
+ return packageValueForCallResult(SETTINGS_TYPE_SYSTEM, name, requestingUserId,
+ setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_PUT_CONFIG: {
@@ -2311,8 +2315,8 @@ public class SettingsProvider extends ContentProvider {
"get/set setting for user", null);
}
- private Bundle packageValueForCallResult(@Nullable Setting setting,
- boolean trackingGeneration) {
+ private Bundle packageValueForCallResult(int type, @NonNull String name, int userId,
+ @Nullable Setting setting, boolean trackingGeneration) {
if (!trackingGeneration) {
if (setting == null || setting.isNull()) {
return NULL_SETTING_BUNDLE;
@@ -2321,24 +2325,36 @@ public class SettingsProvider extends ContentProvider {
}
Bundle result = new Bundle();
result.putString(Settings.NameValueTable.VALUE,
- !setting.isNull() ? setting.getValue() : null);
+ (setting != null && !setting.isNull()) ? setting.getValue() : null);
- if (setting != null && !setting.isNull()) {
- // No need to track generation if the setting doesn't exist
+ if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
+ // Don't track generation for non-existent settings unless the name is predefined
synchronized (mLock) {
- mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey(),
- setting.getName());
+ mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
+ SettingsState.makeKey(type, userId), name);
}
}
return result;
}
+ private boolean isSettingPreDefined(String name, int type) {
+ if (type == SETTINGS_TYPE_GLOBAL) {
+ return sAllGlobalSettings.contains(name);
+ } else if (type == SETTINGS_TYPE_SECURE) {
+ return sAllSecureSettings.contains(name);
+ } else if (type == SETTINGS_TYPE_SYSTEM) {
+ return sAllSystemSettings.contains(name);
+ } else {
+ return false;
+ }
+ }
+
private Bundle packageValuesForCallResult(String prefix,
@NonNull HashMap<String, String> keyValues, boolean trackingGeneration) {
Bundle result = new Bundle();
result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
- if (trackingGeneration && !keyValues.isEmpty()) {
- // No need to track generation if the namespace is empty
+ if (trackingGeneration) {
+ // Track generation even if the namespace is empty because this is for system apps
synchronized (mLock) {
mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 596ff0e9f380..90bec4292313 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -646,7 +646,7 @@
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
- <!-- Permission required for CTS test - CtsPermission5TestCases -->
+ <!-- Permission required for CTS test - CtsAttributionSourceTestCases -->
<uses-permission android:name="android.permission.RENOUNCE_PERMISSIONS" />
<!-- Permission required for CTS test - android.widget.cts.ToastTest -->
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9d469611479a..b236ac5af104 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -449,6 +449,7 @@ systemui_optimized_java_defaults {
enabled: true,
optimize: true,
shrink: true,
+ shrink_resources: true,
proguard_compatibility: false,
proguard_flags_files: ["proguard.flags"],
},
diff --git a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml b/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml
deleted file mode 100644
index 4ff2967b5ddf..000000000000
--- a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<com.android.systemui.statusbar.notification.fsi.FsiChromeView android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="50dp"
- android:orientation="vertical"
- xmlns:android="http://schemas.android.com/apk/res/android">
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:id="@+id/fsi_chrome"
- android:layout_height="50dp"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/fsi_app_icon"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:contentDescription="@null" />
-
- <TextView
- android:id="@+id/fsi_app_name"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:padding="10dp"
- android:textSize="22dp"
- android:gravity="center"
- android:textColor="#FFFFFF"
- android:text="AppName" />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <Button
- android:id="@+id/fsi_fullscreen_button"
- android:layout_width="100dp"
- android:layout_height="match_parent"
- android:text="fullscreen" />
-
- <Button
- android:id="@+id/fsi_dismiss_button"
- android:layout_width="100dp"
- android:layout_height="match_parent"
- android:text="dismiss" />
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.notification.fsi.FsiChromeView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 8ba1ff38fd63..37b8ae0f40c4 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -76,7 +76,10 @@
android:layout_height="48dp"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:padding="8dp" />
+ android:padding="8dp"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
</com.android.systemui.statusbar.notification.row.AppControlView>
<!-- ChannelRows get added dynamically -->
@@ -101,7 +104,7 @@
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
- style="@style/TextAppearance.NotificationInfo.Button"/>
+ style="@style/Widget.Dialog.Button"/>
<TextView
android:id="@+id/done_button"
android:text="@string/inline_ok_button"
@@ -113,7 +116,7 @@
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:layout_alignParentEnd="true"
- style="@style/TextAppearance.NotificationInfo.Button"/>
+ style="@style/Widget.Dialog.Button"/>
</RelativeLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index d03cd7e87a2d..190f9994b1dc 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -85,6 +85,9 @@
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"
/>
</LinearLayout>
</com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ac07d5647a3e..76f6f8aefc3a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -257,9 +257,6 @@
<!-- Radius for notifications corners with adjacent notifications -->
<dimen name="notification_corner_radius_small">4dp</dimen>
- <!-- Vertical padding of the FSI container -->
- <dimen name="fsi_chrome_vertical_padding">80dp</dimen>
-
<!-- the padding of the shelf icon container -->
<dimen name="shelf_icon_container_padding">13dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index b92715516a75..8690b36c12d7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -62,7 +62,7 @@ public class PreviewPositionHelper {
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
- int taskbarSize, boolean isTablet,
+ int taskbarSize, boolean isLargeScreen,
int currentRotation, boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
@@ -95,7 +95,7 @@ public class PreviewPositionHelper {
canvasScreenRatio = (float) canvasWidth / screenWidthPx;
}
scaledTaskbarSize = taskbarSize * canvasScreenRatio;
- thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+ thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0;
float scale = thumbnailData.scale;
final float thumbnailScale;
@@ -103,7 +103,7 @@ public class PreviewPositionHelper {
// Landscape vs portrait change.
// Note: Disable rotation in grid layout.
boolean windowingModeSupportsRotation =
- thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isTablet;
+ thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isLargeScreen;
isOrientationDifferent = isOrientationChange(deltaRotate)
&& windowingModeSupportsRotation;
if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 77a13bd91b90..751a3f8458bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -137,7 +137,7 @@ public class Utilities {
/** @return whether or not {@param context} represents that of a large screen device or not */
@TargetApi(Build.VERSION_CODES.R)
- public static boolean isTablet(Context context) {
+ public static boolean isLargeScreen(Context context) {
final WindowManager windowManager = context.getSystemService(WindowManager.class);
final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 7d39c4aaacbf..dd60647021ff 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
+import android.os.SystemProperties;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicyConstants;
@@ -115,6 +116,9 @@ public class QuickStepContract {
public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
// Device dreaming state
public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+ // Whether the back gesture is allowed (or ignored) by the Shade
+ public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
+ "persist.wm.debug.shade_allow_back_gesture", false);
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -243,9 +247,14 @@ public class QuickStepContract {
sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
}
// Disable when in immersive, or the notifications are interactive
- int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+
+ // EdgeBackGestureHandler ignores Back gesture when SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED.
+ // To allow Shade to respond to Back, we're bypassing this check (behind a flag).
+ if (!ALLOW_BACK_GESTURE_IN_SHADE) {
+ disableFlags |= SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+ }
+
return (sysuiStateFlags & disableFlags) != 0;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 90f44a75b005..ae6861812b4c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -22,10 +22,7 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -46,6 +43,8 @@ import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionInfo.Change;
+import com.android.wm.shell.util.TransitionUtil;
+
import java.util.ArrayList;
import java.util.function.Predicate;
@@ -75,7 +74,7 @@ public class RemoteAnimationTargetCompat {
private static void setupLeash(@NonNull SurfaceControl leash,
@NonNull TransitionInfo.Change change, int layer,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
- boolean isOpening = info.getType() == TRANSIT_OPEN || info.getType() == TRANSIT_TO_FRONT;
+ final boolean isOpening = TransitionUtil.isOpeningType(info.getType());
// Put animating stuff above this line and put static stuff below it.
int zSplitLine = info.getChanges().size();
// changes should be ordered top-to-bottom in z
@@ -88,7 +87,7 @@ public class RemoteAnimationTargetCompat {
absBounds.top - info.getRootOffset().y);
// Put all the OPEN/SHOW on top
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
+ if (TransitionUtil.isOpeningType(mode)) {
if (isOpening) {
t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
@@ -99,7 +98,7 @@ public class RemoteAnimationTargetCompat {
// put on bottom and leave it visible
t.setLayer(leash, zSplitLine - layer);
}
- } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
+ } else if (TransitionUtil.isClosingType(mode)) {
if (isOpening) {
// put on bottom and leave visible
t.setLayer(leash, zSplitLine - layer);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 80b97588279f..70a36ce7e904 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -19,11 +19,7 @@ package com.android.systemui.shared.system;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.newTarget;
@@ -50,6 +46,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
@@ -183,7 +180,7 @@ public class RemoteTransitionCompat {
final RemoteAnimationTarget target = newTarget(change,
info.getChanges().size() - i, info, t, mLeashMap);
apps.add(target);
- if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
+ if (TransitionUtil.isClosingType(change.getMode())) {
// raise closing (pausing) task to "above" layer so it isn't covered
t.setLayer(target.leash, info.getChanges().size() * 3 - i);
mPausingTasks.add(new TaskState(change, target.leash));
@@ -200,8 +197,7 @@ public class RemoteTransitionCompat {
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
mRecentsTask = taskInfo.token;
mRecentsTaskId = taskInfo.taskId;
- } else if (change.getMode() == TRANSIT_OPEN
- || change.getMode() == TRANSIT_TO_FRONT) {
+ } else if (TransitionUtil.isOpeningType(change.getMode())) {
mOpeningTasks.add(new TaskState(change, target.leash));
}
}
@@ -227,7 +223,7 @@ public class RemoteTransitionCompat {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
final boolean isLeafTask = leafTaskFilter.test(change);
- if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+ if (TransitionUtil.isOpeningType(change.getMode())) {
if (mRecentsTask.equals(change.getContainer())) {
recentsOpening = change;
} else if (isLeafTask) {
@@ -240,8 +236,7 @@ public class RemoteTransitionCompat {
}
openingTasks.add(change);
}
- } else if (change.getMode() == TRANSIT_CLOSE
- || change.getMode() == TRANSIT_TO_BACK) {
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
if (mRecentsTask.equals(change.getContainer())) {
foundRecentsClosing = true;
} else if (isLeafTask) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index b53b868025e8..f4c581552bc4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -21,8 +21,6 @@ import android.util.Slog;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -62,7 +60,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
DozeParameters dozeParameters,
- FeatureFlags featureFlags,
ScreenOffAnimationController screenOffAnimationController,
KeyguardLogger logger) {
super(keyguardStatusView);
@@ -73,8 +70,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
logger.getBuffer());
- mKeyguardVisibilityHelper.setOcclusionTransitionFlagEnabled(
- featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION));
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index eb4b05b42b91..f038c69788b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,6 +29,7 @@ import static android.hardware.biometrics.BiometricConstants.LockoutMode;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -139,9 +140,9 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
+import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -152,6 +153,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
+import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -823,6 +825,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ private void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ Assert.isMainThread();
+ Trace.beginSection("KeyGuardUpdateMonitor#onBiometricDetected");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricDetected(userId, biometricSourceType, isStrongBiometric);
+ }
+ }
+ Trace.endSection();
+ }
+
@VisibleForTesting
protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) {
Assert.isMainThread();
@@ -899,6 +914,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ private void handleBiometricDetected(int authUserId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ Trace.beginSection("KeyGuardUpdateMonitor#handlerBiometricDetected");
+ onBiometricDetected(authUserId, biometricSourceType, isStrongBiometric);
+ if (biometricSourceType == FINGERPRINT) {
+ mLogger.logFingerprintDetected(authUserId, isStrongBiometric);
+ } else if (biometricSourceType == FACE) {
+ mLogger.logFaceDetected(authUserId, isStrongBiometric);
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ }
+
+ Trace.endSection();
+ }
+
private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
@@ -950,8 +979,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void onFingerprintCancelNotReceived() {
mLogger.e("Fp cancellation not received, transitioning to STOPPED");
+ final boolean wasCancellingRestarting = mFingerprintRunningState
+ == BIOMETRIC_STATE_CANCELLING_RESTARTING;
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
- KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
+ if (wasCancellingRestarting) {
+ KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ } else {
+ KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
+ }
}
private void handleFingerprintError(int msgId, String errString) {
@@ -1038,6 +1073,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE),
getBiometricLockoutDelay());
} else {
+ boolean temporaryLockoutReset = wasLockout && !mFingerprintLockedOut;
+ if (temporaryLockoutReset) {
+ mLogger.d("temporaryLockoutReset - stopListeningForFingerprint() to stop"
+ + " detectFingerprint");
+ stopListeningForFingerprint();
+ }
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -1747,10 +1788,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
+ private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback =
+ (sensorId, userId, isStrongBiometric) -> {
+ // Trigger the fingerprint detected path so the bouncer can be shown
+ handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+ };
+
private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
= (sensorId, userId, isStrongBiometric) -> {
- // Trigger the face success path so the bouncer can be shown
- handleFaceAuthenticated(userId, isStrongBiometric);
+ // Trigger the face detected path so the bouncer can be shown
+ handleBiometricDetected(userId, FACE, isStrongBiometric);
};
@VisibleForTesting
@@ -2783,8 +2830,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
&& shouldListenBouncerState && shouldListenUdfpsState
- && shouldListenSideFpsState
- && !isFingerprintLockedOut();
+ && shouldListenSideFpsState;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -2939,11 +2985,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.v("startListeningForFingerprint - detect");
mFpm.detectFingerprint(
mFingerprintCancelSignal,
- (sensorId, user, isStrongBiometric) -> {
- mLogger.d("fingerprint detected");
- // Trigger the fingerprint success path so the bouncer can be shown
- handleFingerprintAuthenticated(user, isStrongBiometric);
- },
+ mFingerprintDetectionCallback,
new FingerprintAuthenticateOptions.Builder()
.setUserId(userId)
.build());
@@ -2983,6 +3025,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (unlockPossible) {
mFaceCancelSignal = new CancellationSignal();
+ final FaceAuthenticateOptions faceAuthenticateOptions =
+ new SysUiFaceAuthenticateOptions(
+ userId,
+ faceAuthUiEvent,
+ faceAuthUiEvent == FACE_AUTH_UPDATED_STARTED_WAKING_UP
+ ? faceAuthUiEvent.getExtraInfo()
+ : WAKE_REASON_UNKNOWN
+ ).toFaceAuthenticateOptions();
// This would need to be updated for multi-sensor devices
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).supportsFaceDetection;
@@ -2993,9 +3043,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Run face detection. (If a face is detected, show the bouncer.)
mLogger.v("startListeningForFace - detect");
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback,
- new FaceAuthenticateOptions.Builder()
- .setUserId(userId)
- .build());
+ faceAuthenticateOptions);
} else {
// Don't run face detection. Instead, inform the user
// face auth is unavailable and how to proceed.
@@ -3014,7 +3062,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean isBypassEnabled = mKeyguardBypassController != null
&& mKeyguardBypassController.isBypassEnabled();
mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
- mFaceAuthenticationCallback, null /* handler */, userId);
+ mFaceAuthenticationCallback, null /* handler */,
+ faceAuthenticateOptions);
}
setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 38f3e5065eec..0d4889a4c39f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -214,7 +214,7 @@ public class KeyguardUpdateMonitorCallback {
public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
/**
- * Called when a biometric is recognized.
+ * Called when a biometric is authenticated.
* @param userId the user id for which the biometric sample was authenticated
* @param biometricSourceType
*/
@@ -222,6 +222,14 @@ public class KeyguardUpdateMonitorCallback {
boolean isStrongBiometric) { }
/**
+ * Called when a biometric is detected but not successfully authenticated.
+ * @param userId the user id for which the biometric sample was detected
+ * @param biometricSourceType
+ */
+ public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) { }
+
+ /**
* Called when biometric authentication provides help string (e.g. "Try again")
* @param msgId
* @param helpString
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 7e48193bfc62..a678edc0eb06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -28,7 +28,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -49,7 +48,6 @@ public class KeyguardVisibilityHelper {
private boolean mAnimateYPos;
private boolean mKeyguardViewVisibilityAnimating;
private boolean mLastOccludedState = false;
- private boolean mIsUnoccludeTransitionFlagEnabled = false;
private final AnimationProperties mAnimationProperties = new AnimationProperties();
private final LogBuffer mLogBuffer;
@@ -77,10 +75,6 @@ public class KeyguardVisibilityHelper {
return mKeyguardViewVisibilityAnimating;
}
- public void setOcclusionTransitionFlagEnabled(boolean enabled) {
- mIsUnoccludeTransitionFlagEnabled = enabled;
- }
-
/**
* Set the visibility of a keyguard view based on some new state.
*/
@@ -156,24 +150,9 @@ public class KeyguardVisibilityHelper {
// since it may need to be cancelled due to keyguard lifecycle events.
mScreenOffAnimationController.animateInKeyguard(
mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
- } else if (!mIsUnoccludeTransitionFlagEnabled && mLastOccludedState && !isOccluded) {
- // An activity was displayed over the lock screen, and has now gone away
- log("Unoccluded transition");
- mView.setVisibility(View.VISIBLE);
- mView.setAlpha(0f);
-
- mView.animate()
- .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1f)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
- .start();
} else {
log("Direct set Visibility to VISIBLE");
mView.setVisibility(View.VISIBLE);
- if (!mIsUnoccludeTransitionFlagEnabled) {
- mView.setAlpha(1f);
- }
}
} else {
log("Direct set Visibility to GONE");
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index c414c088529c..e53f6adb62a4 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -167,6 +167,20 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
}, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
}
+ fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) {
+ logBuffer.log(TAG, DEBUG, {
+ int1 = userId
+ bool1 = isStrongBiometric
+ }, {"Face detected: userId: $int1, isStrongBiometric: $bool1"})
+ }
+
+ fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) {
+ logBuffer.log(TAG, DEBUG, {
+ int1 = userId
+ bool1 = isStrongBiometric
+ }, {"Fingerprint detected: userId: $int1, isStrongBiometric: $bool1"})
+ }
+
fun logFingerprintError(msgId: Int, originalErrMsg: String) {
logBuffer.log(TAG, DEBUG, {
str1 = originalErrMsg
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index e698faffd3f6..08efd89029ed 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -85,6 +85,8 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -98,8 +100,6 @@ import java.util.Set;
import javax.inject.Inject;
import javax.inject.Provider;
-import kotlin.Unit;
-
/**
* Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
* appropriate biometric UI (e.g. BiometricDialogView).
@@ -872,7 +872,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
}
/**
- * Stores the callback received from {@link com.android.server.display.DisplayModeDirector}.
+ * Stores the callback received from
+ * {@link com.android.server.display.mode.DisplayModeDirector}.
*
* DisplayModeDirector implements {@link IUdfpsRefreshRateRequestCallback}
* and registers it with this class by calling
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index cef415c8a490..98a3e4b55256 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -130,7 +130,8 @@ final class WirelessChargingLayout extends FrameLayout {
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
// For tablet docking animation, we don't play the background scrim.
- if (!Utilities.isTablet(context)) {
+ // TODO(b/270524780): use utility to check for tablet instead.
+ if (!Utilities.isLargeScreen(context)) {
ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
"backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 9921b1fcd4bc..b86d419f540f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -47,10 +47,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
-import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo
import com.android.systemui.statusbar.notification.InstantAppNotifier
-import com.android.systemui.statusbar.notification.fsi.FsiChromeViewModelFactory
-import com.android.systemui.statusbar.notification.fsi.FsiChromeViewBinder
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -91,24 +88,6 @@ abstract class SystemUICoreStartableModule {
@ClassKey(ClipboardListener::class)
abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable
- /** Inject into FsiChromeRepo. */
- @Binds
- @IntoMap
- @ClassKey(FsiChromeRepo::class)
- abstract fun bindFSIChromeRepo(sysui: FsiChromeRepo): CoreStartable
-
- /** Inject into FsiChromeWindowViewModel. */
- @Binds
- @IntoMap
- @ClassKey(FsiChromeViewModelFactory::class)
- abstract fun bindFSIChromeWindowViewModel(sysui: FsiChromeViewModelFactory): CoreStartable
-
- /** Inject into FsiChromeWindowBinder. */
- @Binds
- @IntoMap
- @ClassKey(FsiChromeViewBinder::class)
- abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable
-
/** Inject into GlobalActionsComponent. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt
new file mode 100644
index 000000000000..1390b4db3576
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.devicepolicy
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+import android.content.ComponentName
+
+/** Returns true if the admin of [userId] disallows keyguard shortcuts. */
+fun DevicePolicyManager.areKeyguardShortcutsDisabled(
+ admin: ComponentName? = null,
+ userId: Int
+): Boolean {
+ val flags = getKeyguardDisabledFeatures(admin, userId)
+ return flags and KEYGUARD_DISABLE_SHORTCUTS_ALL == KEYGUARD_DISABLE_SHORTCUTS_ALL ||
+ flags and KEYGUARD_DISABLE_FEATURES_ALL == KEYGUARD_DISABLE_FEATURES_ALL
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f32ea7147a6e..cbbd3e6bb747 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -64,9 +64,6 @@ object Flags {
// TODO(b/259130119): Tracking Bug
val FSI_ON_DND_UPDATE = releasedFlag(259130119, "fsi_on_dnd_update")
- // TODO(b/265804648): Tracking Bug
- @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
-
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = releasedFlag(111, "instant_voice_reply")
@@ -87,9 +84,6 @@ object Flags {
val NOTIFICATION_GROUP_DISMISSAL_ANIMATION =
releasedFlag(259217907, "notification_group_dismissal_animation")
- // TODO(b/257506350): Tracking Bug
- @JvmField val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
-
@JvmField
val SIMPLIFIED_APPEAR_FRACTION =
unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true)
@@ -155,7 +149,7 @@ object Flags {
// TODO(b/255618149): Tracking Bug
@JvmField
val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
- unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = true)
+ releasedFlag(216, "customizable_lock_screen_quick_affordances")
/** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
// TODO(b/256513609): Tracking Bug
@@ -187,11 +181,7 @@ object Flags {
// TODO(b/262780002): Tracking Bug
@JvmField
- val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = true)
-
- /** A different path for unocclusion transitions back to keyguard */
- // TODO(b/262859270): Tracking Bug
- @JvmField val UNOCCLUSION_TRANSITION = releasedFlag(223, "unocclusion_transition")
+ val REVAMPED_WALLPAPER_UI = releasedFlag(222, "revamped_wallpaper_ui")
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
@@ -214,10 +204,9 @@ object Flags {
// TODO(b/266242192): Tracking Bug
@JvmField
val LOCK_SCREEN_LONG_PRESS_ENABLED =
- unreleasedFlag(
+ releasedFlag(
228,
- "lock_screen_long_press_enabled",
- teamfood = true,
+ "lock_screen_long_press_enabled"
)
// 300 - power menu
@@ -383,6 +372,9 @@ object Flags {
// TODO(b/265045965): Tracking Bug
val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
+ @JvmField
+ val ENABLE_LOW_LIGHT_CLOCK_UNDOCKED = unreleasedFlag(1004, "enable_low_light_clock_undocked")
+
// 1100 - windowing
@Keep
@JvmField
@@ -491,9 +483,9 @@ object Flags {
val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
- // TODO(b/255697805): Tracking Bug
+ // TODO(b/270987164): Tracking Bug
@JvmField
- val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = false)
+ val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = true)
// TODO(b/263826204): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 0ca9115723a3..57c4b36b8b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -945,9 +945,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
return false
}
- // We don't do the shared element on tablets because they're large and the smartspace has to
- // fly across large distances, which is distracting.
- if (Utilities.isTablet(context)) {
+ // We don't do the shared element on large screens because the smartspace has to fly across
+ // large distances, which is distracting.
+ if (Utilities.isLargeScreen(context)) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 02bee3efbe2f..2ad1ab722d55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -127,8 +127,6 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -522,8 +520,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private CentralSurfaces mCentralSurfaces;
- private boolean mUnocclusionTransitionFlagEnabled = false;
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -970,9 +966,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- if (!mUnocclusionTransitionFlagEnabled) {
- setOccluded(true /* isOccluded */, true /* animate */);
- }
if (apps == null || apps.length == 0 || apps[0] == null) {
if (DEBUG) {
Log.d(TAG, "No apps provided to the OccludeByDream runner; "
@@ -1023,7 +1016,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onAnimationEnd(Animator animation) {
try {
- if (!mIsCancelled && mUnocclusionTransitionFlagEnabled) {
+ if (!mIsCancelled) {
// We're already on the main thread, don't queue this call
handleSetOccluded(true /* isOccluded */,
false /* animate */);
@@ -1200,7 +1193,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- FeatureFlags featureFlags,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -1259,7 +1251,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS;
mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS;
- mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
}
public void userActivity() {
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 98d3570106ce..47ef0fac17ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -39,7 +39,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -113,7 +112,6 @@ public class KeyguardModule {
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- FeatureFlags featureFlags,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -144,7 +142,6 @@ public class KeyguardModule {
screenOnCoordinator,
interactionJankMonitor,
dreamOverlayStateController,
- featureFlags,
shadeController,
notificationShadeWindowController,
activityLaunchAnimator,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index dfbe1c216847..9b5f7f606c60 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -26,6 +26,7 @@ import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -410,16 +411,10 @@ constructor(
)
}
- private suspend fun isFeatureDisabledByDevicePolicy(): Boolean {
- val flags =
- withContext(backgroundDispatcher) {
- devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)
- }
- val flagsToCheck =
- DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or
- DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
- return flagsToCheck and flags != 0
- }
+ private suspend fun isFeatureDisabledByDevicePolicy(): Boolean =
+ withContext(backgroundDispatcher) {
+ devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId)
+ }
companion object {
private const val TAG = "KeyguardQuickAffordanceInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
new file mode 100644
index 000000000000..a79513ebd867
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UDFPS_POINTER_DOWN
+import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UNKNOWN
+import android.hardware.face.FaceAuthenticateOptions.AuthenticateReason
+import android.os.PowerManager
+import android.os.PowerManager.WAKE_REASON_UNKNOWN
+import android.util.Log
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+
+/**
+ * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in
+ * [FaceAuthenticateOptions].
+ */
+data class SysUiFaceAuthenticateOptions(
+ val userId: Int,
+ private val faceAuthUiEvent: UiEventLogger.UiEventEnum,
+ @PowerManager.WakeReason val wakeReason: Int = WAKE_REASON_UNKNOWN
+) {
+ val authenticateReason = setAuthenticateReason(faceAuthUiEvent)
+
+ /**
+ * The [FaceAuthUiEvent] for this operation. This method converts the UiEvent to the framework
+ * [AuthenticateReason].
+ */
+ @AuthenticateReason
+ fun setAuthenticateReason(uiEvent: UiEventLogger.UiEventEnum): Int {
+ return when (uiEvent) {
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP -> {
+ AUTHENTICATE_REASON_STARTED_WAKING_UP
+ }
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN -> {
+ AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN
+ }
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED -> {
+ AUTHENTICATE_REASON_ASSISTANT_VISIBLE
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN -> {
+ AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED -> {
+ AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED -> {
+ AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED -> {
+ AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER -> {
+ AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER
+ }
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN -> {
+ AUTHENTICATE_REASON_UDFPS_POINTER_DOWN
+ }
+ else -> {
+ Log.e("FaceAuthenticateOptions", " unmapped FaceAuthUiEvent $uiEvent")
+ AUTHENTICATE_REASON_UNKNOWN
+ }
+ }
+ }
+
+ /** Builds the instance. */
+ fun toFaceAuthenticateOptions(): FaceAuthenticateOptions {
+ return FaceAuthenticateOptions.Builder()
+ .setUserId(userId)
+ .setAuthenticateReason(authenticateReason)
+ .setWakeReason(wakeReason)
+ .build()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index d4991f90a86b..9b9d561b5180 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -33,7 +33,7 @@ import com.android.systemui.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
-import com.android.systemui.shared.recents.utilities.Utilities.isTablet
+import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
/**
* Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData].
@@ -150,9 +150,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
val displayWidthPx = windowMetrics.bounds.width()
val displayHeightPx = windowMetrics.bounds.height()
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
- val isTablet = isTablet(context)
+ val isLargeScreen = isLargeScreen(context)
val taskbarSize =
- if (isTablet) {
+ if (isLargeScreen) {
resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
} else {
0
@@ -166,7 +166,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
displayWidthPx,
displayHeightPx,
taskbarSize,
- isTablet,
+ isLargeScreen,
currentRotation,
isRtl
)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 88d5eaaff216..1c901540ed39 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -23,7 +23,7 @@ import android.view.WindowManager
import com.android.internal.R as AndroidR
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
-import com.android.systemui.shared.recents.utilities.Utilities.isTablet
+import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -61,8 +61,8 @@ constructor(
val width = windowMetrics.bounds.width()
var height = maximumWindowHeight
- val isTablet = isTablet(context)
- if (isTablet) {
+ val isLargeScreen = isLargeScreen(context)
+ if (isLargeScreen) {
val taskbarSize =
context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d5d73258bb08..4db1da3f1c95 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -41,7 +41,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSE
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -1792,7 +1792,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private void setNavigationIconHints(int hints) {
if (hints == mNavigationIconHints) return;
- if (!isTablet(mContext)) {
+ if (!isLargeScreen(mContext)) {
// All IME functions handled by launcher via Sysui flags for large screen
final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
final boolean oldBackAlt =
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3c1746532a02..63d977ed33a7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -21,7 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -91,7 +91,7 @@ public class NavigationBarController implements
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
- @VisibleForTesting boolean mIsTablet;
+ @VisibleForTesting boolean mIsLargeScreen;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -138,16 +138,16 @@ public class NavigationBarController implements
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController, lightBarController, pipOptional,
backAnimation.orElse(null), taskStackChangeListeners);
- mIsTablet = isTablet(mContext);
+ mIsLargeScreen = isLargeScreen(mContext);
dumpManager.registerDumpable(this);
}
@Override
public void onConfigChanged(Configuration newConfig) {
- boolean isOldConfigTablet = mIsTablet;
- mIsTablet = isTablet(mContext);
+ boolean isOldConfigLargeScreen = mIsLargeScreen;
+ mIsLargeScreen = isLargeScreen(mContext);
boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
- boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
+ boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
// TODO(b/243765256): Disable this logging once b/243765256 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
@@ -235,8 +235,9 @@ public class NavigationBarController implements
/** @return {@code true} if taskbar is enabled, false otherwise */
private boolean initializeTaskbarIfNecessary() {
- // Enable for tablet or (phone AND flag is set); assuming phone = !mIsTablet
- boolean taskbarEnabled = mIsTablet || mFeatureFlags.isEnabled(Flags.HIDE_NAVBAR_WINDOW);
+ // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
+ boolean taskbarEnabled = mIsLargeScreen || mFeatureFlags.isEnabled(
+ Flags.HIDE_NAVBAR_WINDOW);
if (taskbarEnabled) {
Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
@@ -258,7 +259,7 @@ public class NavigationBarController implements
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsTablet = isTablet(mContext);
+ mIsLargeScreen = isLargeScreen(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -470,7 +471,7 @@ public class NavigationBarController implements
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mIsTablet=" + mIsTablet);
+ pw.println("mIsLargeScreen=" + mIsLargeScreen);
pw.println("mNavMode=" + mNavMode);
for (int i = 0; i < mNavigationBars.size(); i++) {
if (i > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index be615d63a3d7..c65f0aaab91f 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -17,20 +17,26 @@
package com.android.systemui.notetask
import android.app.KeyguardManager
+import android.app.admin.DevicePolicyManager
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.os.Build
import android.os.UserManager
import android.util.Log
-import com.android.internal.logging.UiEvent
-import com.android.internal.logging.UiEventLogger
+import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
+import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
import java.util.Optional
+import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
/**
@@ -41,18 +47,42 @@ import javax.inject.Inject
* Currently, we only support a single task per time.
*/
@SysUISingleton
-internal class NoteTaskController
+class NoteTaskController
@Inject
constructor(
private val context: Context,
private val resolver: NoteTaskInfoResolver,
+ private val eventLogger: NoteTaskEventLogger,
private val optionalBubbles: Optional<Bubbles>,
- private val optionalKeyguardManager: Optional<KeyguardManager>,
private val optionalUserManager: Optional<UserManager>,
+ private val optionalKeyguardManager: Optional<KeyguardManager>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
- private val uiEventLogger: UiEventLogger,
+ private val devicePolicyManager: DevicePolicyManager,
+ private val userTracker: UserTracker,
) {
+ @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>()
+
+ /** @see BubbleExpandListener */
+ fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) {
+ if (!isEnabled) return
+
+ if (key != Bubble.KEY_APP_BUBBLE) return
+
+ val info = infoReference.getAndSet(null)
+
+ // Safe guard mechanism, this callback should only be called for app bubbles.
+ if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return
+
+ if (isExpanding) {
+ logDebug { "onBubbleExpandChanged - expanding: $info" }
+ eventLogger.logNoteTaskOpened(info)
+ } else {
+ logDebug { "onBubbleExpandChanged - collapsing: $info" }
+ eventLogger.logNoteTaskClosed(info)
+ }
+ }
+
/**
* Shows a note task. How the task is shown will depend on when the method is invoked.
*
@@ -69,32 +99,62 @@ constructor(
* That will let users open other apps in full screen, and take contextual notes.
*/
@JvmOverloads
- fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) {
-
+ fun showNoteTask(
+ entryPoint: NoteTaskEntryPoint,
+ isInMultiWindowMode: Boolean = false,
+ ) {
if (!isEnabled) return
val bubbles = optionalBubbles.getOrNull() ?: return
- val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
val userManager = optionalUserManager.getOrNull() ?: return
+ val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
// TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
if (!userManager.isUserUnlocked) return
- val noteTaskInfo = resolver.resolveInfo() ?: return
+ val isKeyguardLocked = keyguardManager.isKeyguardLocked
+ // KeyguardQuickAffordanceInteractor blocks the quick affordance from showing in the
+ // keyguard if it is not allowed by the admin policy. Here we block any other way to show
+ // note task when the screen is locked.
+ if (
+ isKeyguardLocked &&
+ devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId)
+ ) {
+ logDebug { "Enterprise policy disallows launching note app when the screen is locked." }
+ return
+ }
+
+ val info =
+ resolver.resolveInfo(
+ entryPoint = entryPoint,
+ isInMultiWindowMode = isInMultiWindowMode,
+ isKeyguardLocked = isKeyguardLocked,
+ )
+ ?: return
- uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) }
+ infoReference.set(info)
// TODO(b/266686199): We should handle when app not available. For now, we log.
- val intent = noteTaskInfo.toCreateNoteIntent()
+ val intent = createNoteIntent(info)
try {
- if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
- context.startActivity(intent)
- } else {
- bubbles.showOrHideAppBubble(intent)
+ logDebug { "onShowNoteTask - start: $info" }
+ when (info.launchMode) {
+ is NoteTaskLaunchMode.AppBubble -> {
+ bubbles.showOrHideAppBubble(intent)
+ // App bubble logging happens on `onBubbleExpandChanged`.
+ logDebug { "onShowNoteTask - opened as app bubble: $info" }
+ }
+ is NoteTaskLaunchMode.Activity -> {
+ context.startActivity(intent)
+ eventLogger.logNoteTaskOpened(info)
+ logDebug { "onShowNoteTask - opened as activity: $info" }
+ }
}
+ logDebug { "onShowNoteTask - success: $info" }
} catch (e: ActivityNotFoundException) {
- Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e)
+ logDebug { "onShowNoteTask - failed: $info" }
}
+ logDebug { "onShowNoteTask - compoleted: $info" }
}
/**
@@ -119,41 +179,12 @@ constructor(
enabledState,
PackageManager.DONT_KILL_APP,
)
- }
-
- /** IDs of UI events accepted by [showNoteTask]. */
- enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
- NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
- /* ktlint-disable max-line-length */
- @UiEvent(
- doc =
- "User opened a note by pressing the stylus tail button while the screen was unlocked."
- )
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
- @UiEvent(
- doc =
- "User opened a note by pressing the stylus tail button while the screen was locked."
- )
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
- @UiEvent(doc = "User opened a note by tapping on an app shortcut.")
- NOTE_OPENED_VIA_SHORTCUT(1297);
-
- override fun getId() = _id
+ logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
}
companion object {
- private val TAG = NoteTaskController::class.simpleName.orEmpty()
-
- private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent {
- return Intent(ACTION_CREATE_NOTE)
- .setPackage(packageName)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
- // was used to start it.
- .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true)
- }
+ val TAG = NoteTaskController::class.simpleName.orEmpty()
// TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
const val NOTE_TASK_KEY_EVENT = 311
@@ -165,3 +196,17 @@ constructor(
const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
}
}
+
+private fun createNoteIntent(info: NoteTaskInfo): Intent =
+ Intent(NoteTaskController.ACTION_CREATE_NOTE)
+ .setPackage(info.packageName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
+ // was used to start it.
+ .putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true)
+
+private inline fun logDebug(message: () -> String) {
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(NoteTaskController.TAG, message())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt
index e0bf1da2f652..a2563919955a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt
@@ -19,4 +19,4 @@ package com.android.systemui.notetask
import javax.inject.Qualifier
/** Key associated with a [Boolean] flag that enables or disables the note task feature. */
-@Qualifier internal annotation class NoteTaskEnabledKey
+@Qualifier annotation class NoteTaskEnabledKey
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
new file mode 100644
index 000000000000..acc537a8eb36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig
+import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import com.android.systemui.screenshot.AppClipsTrampolineActivity
+
+/**
+ * Supported entry points for [NoteTaskController.showNoteTask].
+ *
+ * An entry point represents where the note task has ben called from. In rare cases, it may
+ * represent a "re-entry" (i.e., [APP_CLIPS]).
+ */
+enum class NoteTaskEntryPoint {
+
+ /** @see [LaunchNoteTaskActivity] */
+ WIDGET_PICKER_SHORTCUT,
+
+ /** @see [NoteTaskQuickAffordanceConfig] */
+ QUICK_AFFORDANCE,
+
+ /** @see [NoteTaskInitializer.callbacks] */
+ TAIL_BUTTON,
+
+ /** @see [AppClipsTrampolineActivity] */
+ APP_CLIPS,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
new file mode 100644
index 000000000000..16dd16ee137e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
+import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+import javax.inject.Inject
+
+/**
+ * A wrapper around [UiEventLogger] specialized in the note taking UI events.
+ *
+ * if the accepted [NoteTaskInfo] contains a [NoteTaskInfo.entryPoint], it will be logged as the
+ * correct [NoteTaskUiEvent]. If null, it will be ignored.
+ *
+ * @see NoteTaskController for usage examples.
+ */
+class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) {
+
+ /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */
+ fun logNoteTaskOpened(info: NoteTaskInfo) {
+ val event =
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+ }
+ }
+ WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
+ QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+ APP_CLIPS -> return
+ null -> return
+ }
+ uiEventLogger.log(event, info.uid, info.packageName)
+ }
+
+ /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */
+ fun logNoteTaskClosed(info: NoteTaskInfo) {
+ val event =
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
+ }
+ }
+ WIDGET_PICKER_SHORTCUT -> return
+ QUICK_AFFORDANCE -> return
+ APP_CLIPS -> return
+ null -> return
+ }
+ uiEventLogger.log(event, info.uid, info.packageName)
+ }
+
+ /** IDs of UI events accepted by [NoteTaskController]. */
+ enum class NoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
+ NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
+
+ @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
+
+ @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
+
+ @UiEvent(doc = "User opened a note by tapping on an app shortcut.")
+ NOTE_OPENED_VIA_SHORTCUT(1297),
+
+ @UiEvent(doc = "Note closed via a tail button while device is unlocked")
+ NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311),
+
+ @UiEvent(doc = "Note closed via a tail button while device is locked")
+ NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312);
+
+ override fun getId() = _id
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
new file mode 100644
index 000000000000..28d76474efba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+/** Contextual information required to launch a Note Task by [NoteTaskController]. */
+data class NoteTaskInfo(
+ val packageName: String,
+ val uid: Int,
+ val entryPoint: NoteTaskEntryPoint? = null,
+ val isInMultiWindowMode: Boolean = false,
+ val isKeyguardLocked: Boolean = false,
+) {
+
+ val launchMode: NoteTaskLaunchMode =
+ if (isInMultiWindowMode || isKeyguardLocked) {
+ NoteTaskLaunchMode.Activity
+ } else {
+ NoteTaskLaunchMode.AppBubble
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index bd822d40b950..b5d757c6c287 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -19,51 +19,56 @@ package com.android.systemui.notetask
import android.app.role.RoleManager
import android.content.Context
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
import android.os.UserHandle
import android.util.Log
import javax.inject.Inject
-internal class NoteTaskInfoResolver
+class NoteTaskInfoResolver
@Inject
constructor(
private val context: Context,
private val roleManager: RoleManager,
private val packageManager: PackageManager,
) {
- fun resolveInfo(): NoteTaskInfo? {
+ fun resolveInfo(
+ entryPoint: NoteTaskEntryPoint? = null,
+ isInMultiWindowMode: Boolean = false,
+ isKeyguardLocked: Boolean = false,
+ ): NoteTaskInfo? {
// TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
val user = context.user
val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, user).firstOrNull()
if (packageName.isNullOrEmpty()) return null
- return NoteTaskInfo(packageName, packageManager.getUidOf(packageName, user))
+ return NoteTaskInfo(
+ packageName = packageName,
+ uid = packageManager.getUidOf(packageName, user),
+ entryPoint = entryPoint,
+ isInMultiWindowMode = isInMultiWindowMode,
+ isKeyguardLocked = isKeyguardLocked,
+ )
}
- /** Package name and kernel user-ID of a note-taking app. */
- data class NoteTaskInfo(val packageName: String, val uid: Int)
-
companion object {
private val TAG = NoteTaskInfoResolver::class.simpleName.orEmpty()
- private val EMPTY_APPLICATION_INFO_FLAGS = PackageManager.ApplicationInfoFlags.of(0)!!
+ // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
+ const val ROLE_NOTES = "android.app.role.NOTES"
+
+ private val EMPTY_APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0)!!
/**
* Returns the kernel user-ID of [packageName] for a [user]. Returns zero if the app cannot
* be found.
*/
- private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int {
- val applicationInfo =
- try {
- getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.e(TAG, "Couldn't find notes app UID", e)
- return 0
- }
- return applicationInfo.uid
- }
-
- // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
- const val ROLE_NOTES = "android.app.role.NOTES"
+ private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int =
+ try {
+ getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Couldn't find notes app UID", e)
+ 0
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d40bf2b49975..3f4f8d538bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.systemui.notetask
-import android.app.KeyguardManager
import androidx.annotation.VisibleForTesting
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -28,11 +25,10 @@ import javax.inject.Inject
internal class NoteTaskInitializer
@Inject
constructor(
- private val optionalBubbles: Optional<Bubbles>,
- private val noteTaskController: NoteTaskController,
+ private val controller: NoteTaskController,
private val commandQueue: CommandQueue,
+ private val optionalBubbles: Optional<Bubbles>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
- private val optionalKeyguardManager: Optional<KeyguardManager>,
) {
@VisibleForTesting
@@ -40,29 +36,17 @@ constructor(
object : CommandQueue.Callbacks {
override fun handleSystemKey(keyCode: Int) {
if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) {
- showNoteTask()
+ controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
}
}
}
- private fun showNoteTask() {
- val uiEvent =
- if (optionalKeyguardManager.isKeyguardLocked) {
- NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
- } else {
- NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
- }
- noteTaskController.showNoteTask(uiEvent = uiEvent)
- }
-
fun initialize() {
- if (isEnabled && optionalBubbles.isPresent) {
- commandQueue.addCallback(callbacks)
- }
- noteTaskController.setNoteTaskShortcutEnabled(isEnabled)
+ controller.setNoteTaskShortcutEnabled(isEnabled)
+
+ // Guard against feature not being enabled or mandatory dependencies aren't available.
+ if (!isEnabled || optionalBubbles.isEmpty) return
+
+ commandQueue.addCallback(callbacks)
}
}
-
-private val Optional<KeyguardManager>.isKeyguardLocked: Boolean
- // If there's no KeyguardManager, assume that the keyguard is not locked.
- get() = getOrNull()?.isKeyguardLocked ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
new file mode 100644
index 000000000000..836e103f4d69
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+import android.content.Context
+import com.android.wm.shell.bubbles.Bubbles
+
+/**
+ * Supported ways for launching a note taking experience.
+ *
+ * @see [NoteTaskController.showNoteTask]
+ */
+sealed class NoteTaskLaunchMode {
+
+ /** @see Bubbles.showOrHideAppBubble */
+ object AppBubble : NoteTaskLaunchMode()
+
+ /** @see Context.startActivity */
+ object Activity : NoteTaskLaunchMode()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index b8800a242d06..f16110d7cf5b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -36,7 +36,7 @@ import java.util.Optional
/** Compose all dependencies required by Note Task feature. */
@Module(includes = [NoteTaskQuickAffordanceModule::class])
-internal interface NoteTaskModule {
+interface NoteTaskModule {
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 43869ccda2b1..30660c492baa 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -27,12 +27,12 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
import com.android.systemui.notetask.NoteTaskController
-import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
import com.android.systemui.notetask.NoteTaskEnabledKey
+import com.android.systemui.notetask.NoteTaskEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.flowOf
-internal class NoteTaskQuickAffordanceConfig
+class NoteTaskQuickAffordanceConfig
@Inject
constructor(
context: Context,
@@ -66,7 +66,7 @@ constructor(
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
noteTaskController.showNoteTask(
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+ entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE,
)
return OnTriggeredResult.Handled
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
index 7cb932aa1916..2d63dbcb82fa 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
@@ -22,7 +22,7 @@ import dagger.Module
import dagger.multibindings.IntoSet
@Module
-internal interface NoteTaskQuickAffordanceModule {
+interface NoteTaskQuickAffordanceModule {
@[Binds IntoSet]
fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index 6ab0da6fe3b3..0a1d0089735f 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -36,7 +36,7 @@ import javax.inject.Inject
* href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
* a custom shortcut activity</a>
*/
-internal class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() {
+class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 3ac5bfa09aaa..2b84bf8b4e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -21,11 +21,11 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.android.systemui.notetask.NoteTaskController
-import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
+import com.android.systemui.notetask.NoteTaskEntryPoint
import javax.inject.Inject
/** Activity responsible for launching the note experience, and finish. */
-internal class LaunchNoteTaskActivity
+class LaunchNoteTaskActivity
@Inject
constructor(
private val noteTaskController: NoteTaskController,
@@ -35,8 +35,8 @@ constructor(
super.onCreate(savedInstanceState)
noteTaskController.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
isInMultiWindowMode = isInMultiWindowMode,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT,
)
finish()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 645b1256e5f1..346acf958e51 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,7 +16,7 @@
package com.android.systemui.recents;
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -265,7 +265,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
+ && hasSoftNavigationBar(mContext.getDisplayId()) && !isLargeScreen(mContext)) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index 1946b8eaee5b..eda38e45c98a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -50,6 +50,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.notetask.NoteTaskController;
+import com.android.systemui.notetask.NoteTaskEntryPoint;
import com.android.systemui.settings.UserTracker;
import com.android.wm.shell.bubbles.Bubbles;
@@ -239,9 +240,8 @@ public class AppClipsTrampolineActivity extends Activity {
// Broadcast no longer required, setting it to null.
mKillAppClipsBroadcastIntent = null;
- // Expand the note bubble before returning the result. As App Clips API is only
- // available when in a bubble, isInMultiWindowMode is always false below.
- mNoteTaskController.showNoteTask(false);
+ // Expand the note bubble before returning the result.
+ mNoteTaskController.showNoteTask(NoteTaskEntryPoint.APP_CLIPS);
setResult(RESULT_OK, convertedData);
finish();
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index b36f0d7bacfc..10e2afe0baa9 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -27,6 +27,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.view.animation.DecelerateInterpolator;
@@ -41,7 +42,11 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
public class ScrimDrawable extends Drawable {
private static final String TAG = "ScrimDrawable";
+ private boolean mShouldUseLargeScreenSize;
private final Paint mPaint;
+ private final Path mPath = new Path();
+ private final RectF mBoundsRectF = new RectF();
+
private int mAlpha = 255;
private int mMainColor;
private ValueAnimator mColorAnimation;
@@ -49,11 +54,13 @@ public class ScrimDrawable extends Drawable {
private float mCornerRadius;
private ConcaveInfo mConcaveInfo;
private int mBottomEdgePosition;
+ private float mBottomEdgeRadius = -1;
private boolean mCornerRadiusEnabled;
public ScrimDrawable() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
+ mShouldUseLargeScreenSize = false;
}
/**
@@ -133,6 +140,10 @@ public class ScrimDrawable extends Drawable {
return PixelFormat.TRANSLUCENT;
}
+ public void setShouldUseLargeScreenSize(boolean v) {
+ mShouldUseLargeScreenSize = v;
+ }
+
/**
* Corner radius used by either concave or convex corners.
*/
@@ -191,6 +202,10 @@ public class ScrimDrawable extends Drawable {
invalidateSelf();
}
+ public void setBottomEdgeRadius(float radius) {
+ mBottomEdgeRadius = radius;
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
@@ -198,9 +213,46 @@ public class ScrimDrawable extends Drawable {
if (mConcaveInfo != null) {
drawConcave(canvas);
} else if (mCornerRadiusEnabled && mCornerRadius > 0) {
- canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
- getBounds().bottom,
- /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
+ float topEdgeRadius = mCornerRadius;
+ float bottomEdgeRadius = mBottomEdgeRadius == -1.0 ? mCornerRadius : mBottomEdgeRadius;
+
+ mBoundsRectF.set(getBounds());
+
+ // When the back gesture causes the notification scrim to be scaled down,
+ // this offset "reveals" the rounded bottom edge as it "pulls away".
+ // We must *not* make this adjustment on largescreen shades (where the corner is sharp).
+ if (!mShouldUseLargeScreenSize && mBottomEdgeRadius != -1) {
+ mBoundsRectF.bottom -= bottomEdgeRadius;
+ }
+
+ // We need a box with rounded corners but its lower corners are not rounded on large
+ // screen devices in "portrait" orientation.
+ // Thus, we cannot draw a symmetric rounded rectangle via canvas.drawRoundRect()
+ // and must build a box with different corner radii at the top and at the bottom.
+ // Additionally, when the scrim is pushed to the very bottom of the screen, do not draw
+ // anything (drawing a rounded box with these specifications is not possible).
+ // TODO(b/271030611) perhaps this could be accomplished via Path.addRoundRect instead?
+ if (mBoundsRectF.bottom - mBoundsRectF.top > bottomEdgeRadius) {
+ mPath.reset();
+ mPath.moveTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius);
+ mPath.cubicTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius,
+ mBoundsRectF.right, mBoundsRectF.top,
+ mBoundsRectF.right - topEdgeRadius, mBoundsRectF.top);
+ mPath.lineTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top);
+ mPath.cubicTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top,
+ mBoundsRectF.left, mBoundsRectF.top,
+ mBoundsRectF.left, mBoundsRectF.top + topEdgeRadius);
+ mPath.lineTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius);
+ mPath.cubicTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius,
+ mBoundsRectF.left, mBoundsRectF.bottom,
+ mBoundsRectF.left + bottomEdgeRadius, mBoundsRectF.bottom);
+ mPath.lineTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom);
+ mPath.cubicTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom,
+ mBoundsRectF.right, mBoundsRectF.bottom,
+ mBoundsRectF.right, mBoundsRectF.bottom - bottomEdgeRadius);
+ mPath.close();
+ canvas.drawPath(mPath, mPaint);
+ }
} else {
canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
getBounds().bottom, mPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index f68e0429ef7c..fc89a9e637ec 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -20,6 +20,7 @@ import static java.lang.Float.isNaN;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
@@ -37,6 +38,7 @@ import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.util.LargeScreenUtils;
import java.util.concurrent.Executor;
@@ -102,6 +104,13 @@ public class ScrimView extends View {
@Override
protected void onDraw(Canvas canvas) {
if (mDrawable.getAlpha() > 0) {
+ Resources res = getResources();
+ // Scrim behind notification shade has sharp (not rounded) corners on large screens
+ // which scrim itself cannot know, so we set it here.
+ if (mDrawable instanceof ScrimDrawable) {
+ ((ScrimDrawable) mDrawable).setShouldUseLargeScreenSize(
+ LargeScreenUtils.shouldUseLargeScreenShadeHeader(res));
+ }
mDrawable.draw(canvas);
}
}
@@ -170,6 +179,15 @@ public class ScrimView extends View {
});
}
+ /**
+ * Set corner radius of the bottom edge of the Notification scrim.
+ */
+ public void setBottomEdgeRadius(float radius) {
+ if (mDrawable instanceof ScrimDrawable) {
+ ((ScrimDrawable) mDrawable).setBottomEdgeRadius(radius);
+ }
+ }
+
@VisibleForTesting
Drawable getDrawable() {
return mDrawable;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b502b4d02e00..e75320a1f56b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -293,7 +293,19 @@ public final class NotificationPanelViewController implements Dumpable {
* custom clock animation is in use.
*/
private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000;
+ /**
+ * Whether the Shade should animate to reflect Back gesture progress.
+ * To minimize latency at runtime, we cache this, else we'd be reading it every time
+ * updateQsExpansion() is called... and it's called very often.
+ *
+ * Whenever we change this flag, SysUI is restarted, so it's never going to be "stale".
+ */
+ public final boolean mAnimateBack;
+ /**
+ * The minimum scale to "squish" the Shade and associated elements down to, for Back gesture
+ */
+ public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final Resources mResources;
private final KeyguardStateController mKeyguardStateController;
@@ -361,6 +373,8 @@ public final class NotificationPanelViewController implements Dumpable {
private CentralSurfaces mCentralSurfaces;
private HeadsUpManagerPhone mHeadsUpManager;
private float mExpandedHeight = 0;
+ /** The current squish amount for the predictive back animation */
+ private float mCurrentBackProgress = 0.0f;
private boolean mTracking;
private boolean mHintAnimationRunning;
private KeyguardBottomAreaView mKeyguardBottomArea;
@@ -465,7 +479,7 @@ public final class NotificationPanelViewController implements Dumpable {
private int mPanelAlpha;
private Runnable mPanelAlphaEndAction;
private float mBottomAreaShadeAlpha;
- private final ValueAnimator mBottomAreaShadeAlphaAnimator;
+ final ValueAnimator mBottomAreaShadeAlphaAnimator;
private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
NotificationPanelView::setPanelAlphaInternal,
NotificationPanelView::getCurrentPanelAlpha,
@@ -597,7 +611,6 @@ public final class NotificationPanelViewController implements Dumpable {
private int mLockscreenToDreamingTransitionTranslationY;
private int mGoneToDreamingTransitionTranslationY;
private int mLockscreenToOccludedTransitionTranslationY;
- private boolean mUnocclusionTransitionFlagEnabled = false;
private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
@@ -816,6 +829,7 @@ public final class NotificationPanelViewController implements Dumpable {
mShadeHeaderController = shadeHeaderController;
mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags;
+ mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
mFalsingCollector = falsingCollector;
mPowerManager = powerManager;
mWakeUpCoordinator = coordinator;
@@ -886,7 +900,6 @@ public final class NotificationPanelViewController implements Dumpable {
mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
- mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
updateUserSwitcherFlags();
mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
@@ -1045,62 +1058,50 @@ public final class NotificationPanelViewController implements Dumpable {
mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
controller.setup(mNotificationContainerParent));
- if (mUnocclusionTransitionFlagEnabled) {
- // Dreaming->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
- mDreamingToLockscreenTransition, mMainDispatcher);
- collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
- setTransitionAlpha(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(
- mDreamingToLockscreenTransitionTranslationY),
- setTransitionY(mNotificationStackScrollLayoutController),
- mMainDispatcher);
-
- // Occluded->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
- mOccludedToLockscreenTransition, mMainDispatcher);
- collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
- setTransitionAlpha(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(
- mOccludedToLockscreenTransitionTranslationY),
- setTransitionY(mNotificationStackScrollLayoutController),
- mMainDispatcher);
-
- // Lockscreen->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(),
- mLockscreenToDreamingTransition, mMainDispatcher);
- collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
- setTransitionAlpha(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(
- mLockscreenToDreamingTransitionTranslationY),
- setTransitionY(mNotificationStackScrollLayoutController),
- mMainDispatcher);
-
- // Gone->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
- mGoneToDreamingTransition, mMainDispatcher);
- collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
- setTransitionAlpha(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
- mGoneToDreamingTransitionTranslationY),
- setTransitionY(mNotificationStackScrollLayoutController),
- mMainDispatcher);
-
- // Lockscreen->Occluded
- collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
- mLockscreenToOccludedTransition, mMainDispatcher);
- collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
- setTransitionAlpha(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(
- mLockscreenToOccludedTransitionTranslationY),
- setTransitionY(mNotificationStackScrollLayoutController),
- mMainDispatcher);
- }
+ // Dreaming->Lockscreen
+ collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
+ mDreamingToLockscreenTransition, mMainDispatcher);
+ collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(
+ mDreamingToLockscreenTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+
+ // Occluded->Lockscreen
+ collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
+ mOccludedToLockscreenTransition, mMainDispatcher);
+ collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(
+ mOccludedToLockscreenTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+
+ // Lockscreen->Dreaming
+ collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(),
+ mLockscreenToDreamingTransition, mMainDispatcher);
+ collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(
+ mLockscreenToDreamingTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+
+ // Gone->Dreaming
+ collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
+ mGoneToDreamingTransition, mMainDispatcher);
+ collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
+ mGoneToDreamingTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+
+ // Lockscreen->Occluded
+ collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
+ mLockscreenToOccludedTransition, mMainDispatcher);
+ collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
+ setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(
+ mLockscreenToOccludedTransitionTranslationY),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
}
@VisibleForTesting
@@ -1965,6 +1966,14 @@ public final class NotificationPanelViewController implements Dumpable {
if (mFixedDuration != NO_FIXED_DURATION) {
animator.setDuration(mFixedDuration);
}
+
+ // Reset Predictive Back animation's transform after Shade is completely hidden.
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ resetBackTransformation();
+ }
+ });
}
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -2189,6 +2198,53 @@ public final class NotificationPanelViewController implements Dumpable {
}
}
+ /**
+ * When the back gesture triggers a fully-expanded shade --> QQS shade collapse transition,
+ * the expansionFraction goes down from 1.0 --> 0.0 (collapsing), so the current "squish" amount
+ * (mCurrentBackProgress) must be un-applied from various UI elements in tandem, such that,
+ * as the shade ends up in its half-expanded state (with QQS above), it is back at 100% scale.
+ * Without this, the shade would collapse, and stay squished.
+ */
+ public void adjustBackAnimationScale(float expansionFraction) {
+ if (expansionFraction > 0.0f) { // collapsing
+ float animatedFraction = expansionFraction * mCurrentBackProgress;
+ applyBackScaling(animatedFraction);
+ } else {
+ // collapsed! reset, so that if we re-expand shade, it won't start off "squished"
+ mCurrentBackProgress = 0;
+ }
+ }
+
+ //TODO(b/270981268): allow cancelling back animation mid-flight
+ /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
+ public void onBackPressed() {
+ closeQsIfPossible();
+ }
+ /** Sets back progress. */
+ public void onBackProgressed(float progressFraction) {
+ // TODO: non-linearly transform progress fraction into squish amount (ease-in, linear out)
+ mCurrentBackProgress = progressFraction;
+ applyBackScaling(progressFraction);
+ }
+
+ /** Resets back progress. */
+ public void resetBackTransformation() {
+ mCurrentBackProgress = 0.0f;
+ applyBackScaling(0.0f);
+ }
+
+ /** Scales multiple elements in tandem to achieve the illusion of the QS+Shade shrinking
+ * as a single visual element (used by the Predictive Back Gesture preview animation).
+ * fraction = 0 implies "no scaling", and 1 means "scale down to minimum size (90%)".
+ */
+ public void applyBackScaling(float fraction) {
+ if (mNotificationContainerParent == null) {
+ return;
+ }
+ float scale = MathUtils.lerp(1.0f, SHADE_BACK_ANIM_MIN_SCALE, fraction);
+ mNotificationContainerParent.applyBackScaling(scale, mSplitShadeEnabled);
+ mScrimController.applyBackScaling(scale);
+ }
/** */
public float getLockscreenShadeDragProgress() {
// mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade
@@ -2480,9 +2536,6 @@ public final class NotificationPanelViewController implements Dumpable {
}
private void onExpandingFinished() {
- if (!mUnocclusionTransitionFlagEnabled) {
- mScrimController.onExpandingFinished();
- }
mNotificationStackScrollLayoutController.onExpansionStopped();
mHeadsUpManager.onExpandingFinished();
mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
@@ -4868,6 +4921,11 @@ public final class NotificationPanelViewController implements Dumpable {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) {
+ // Cache the gesture insets now, so we can quickly query them during
+ // ACTION_MOVE and decide whether to intercept events for back gesture anim.
+ mQsController.updateGestureInsetsCache();
+ }
mShadeLog.logMotionEvent(event, "onTouch: down action");
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mMinExpandHeight = 0.0f;
@@ -4917,6 +4975,12 @@ public final class NotificationPanelViewController implements Dumpable {
}
break;
case MotionEvent.ACTION_MOVE:
+ // If the shade is half-collapsed, a horizontal swipe inwards from L/R edge
+ // must be routed to the back gesture (which shows a preview animation).
+ if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack
+ && mQsController.shouldBackBypassQuickSettings(x)) {
+ return false;
+ }
if (isFullyCollapsed()) {
// If panel is fully collapsed, reset haptic effect before adding movement.
mHasVibratedOnOpen = false;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 60fa865b83bc..87350b465895 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -38,8 +38,6 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -132,7 +130,6 @@ public class NotificationShadeWindowViewController {
NotificationInsetsController notificationInsetsController,
AmbientState ambientState,
PulsingGestureListener pulsingGestureListener,
- FeatureFlags featureFlags,
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
@@ -165,10 +162,8 @@ public class NotificationShadeWindowViewController {
keyguardBouncerViewModel,
keyguardBouncerComponentFactory);
- if (featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)) {
- collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
- mLockscreenToDreamingTransition);
- }
+ collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
+ mLockscreenToDreamingTransition);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index f73dde632051..7dff6ea99029 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -20,6 +20,7 @@ import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
@@ -55,6 +56,13 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private QS mQs;
private View mQSContainer;
+ /**
+ * These are used to compute the bounding box containing the shade and the notification scrim,
+ * which is then used to drive the Back gesture animation.
+ */
+ private final Rect mUpperRect = new Rect();
+ private final Rect mBoundingBoxRect = new Rect();
+
@Nullable
private Consumer<Configuration> mConfigurationChangedListener;
@@ -172,4 +180,37 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
public void applyConstraints(ConstraintSet constraintSet) {
constraintSet.applyTo(this);
}
+
+ /**
+ * Scale multiple elements in tandem, for the predictive back animation.
+ * This is how the Shade responds to the Back gesture (by scaling).
+ * Without the common center, individual elements will scale about their respective centers.
+ * Scaling the entire NotificationsQuickSettingsContainer will also resize the shade header
+ * (which we don't want).
+ */
+ public void applyBackScaling(float scale, boolean usingSplitShade) {
+ if (mStackScroller == null || mQSContainer == null) {
+ return;
+ }
+
+ mQSContainer.getBoundsOnScreen(mUpperRect);
+ mStackScroller.getBoundsOnScreen(mBoundingBoxRect);
+ mBoundingBoxRect.union(mUpperRect);
+
+ float cx = mBoundingBoxRect.centerX();
+ float cy = mBoundingBoxRect.centerY();
+
+ mQSContainer.setPivotX(cx);
+ mQSContainer.setPivotY(cy);
+ mQSContainer.setScaleX(scale);
+ mQSContainer.setScaleY(scale);
+
+ // When in large-screen split-shade mode, the notification stack scroller scales correctly
+ // only if the pivot point is at the left edge of the screen (because of its dimensions).
+ // When not in large-screen split-shade mode, we can scale correctly via the (cx,cy) above.
+ mStackScroller.setPivotX(usingSplitShade ? 0.0f : cx);
+ mStackScroller.setPivotY(cy);
+ mStackScroller.setScaleX(scale);
+ mStackScroller.setScaleY(scale);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 099ad9473673..6857f4cc9e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -31,6 +31,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.Log;
@@ -40,6 +41,9 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
@@ -63,6 +67,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -83,10 +88,10 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
-import javax.inject.Inject;
-
import dagger.Lazy;
+import javax.inject.Inject;
+
/** Handles QuickSettings touch handling, expansion and animation state
* TODO (b/264460656) make this dumpable
*/
@@ -223,6 +228,13 @@ public class QuickSettingsController {
private boolean mAnimatorExpand;
/**
+ * The gesture inset currently in effect -- used to decide whether a back gesture should
+ * receive a horizontal swipe inwards from the left/right vertical edge of the screen.
+ * We cache this on ACTION_DOWN, and query it during both ACTION_DOWN and ACTION_MOVE events.
+ */
+ private Insets mCachedGestureInsets;
+
+ /**
* The amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
* shade. This value can also go beyond 1.1 when we're overshooting!
@@ -406,6 +418,7 @@ public class QuickSettingsController {
mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping);
+ updateGestureInsetsCache();
}
// TODO (b/265054088): move this and others to a CoreStartable
@@ -469,6 +482,26 @@ public class QuickSettingsController {
|| touchX > mQsFrame.getX() + mQsFrame.getWidth();
}
+ /**
+ * Computes (and caches) the gesture insets for the current window. Intended to be called
+ * on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events.
+ */
+ public void updateGestureInsetsCache() {
+ WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class);
+ WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
+ mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets(
+ WindowInsets.Type.systemGestures());
+ }
+
+ /**
+ * Returns whether x coordinate lies in the vertical edges of the screen
+ * (the only place where a back gesture can be initiated).
+ */
+ public boolean shouldBackBypassQuickSettings(float touchX) {
+ return (touchX < mCachedGestureInsets.left)
+ || (touchX > mKeyguardStatusBar.getWidth() - mCachedGestureInsets.right);
+ }
+
/** Returns whether touch is within QS area */
private boolean isTouchInQsArea(float x, float y) {
if (isSplitShadeAndTouchXOutsideQs(x)) {
@@ -926,6 +959,10 @@ public class QuickSettingsController {
getHeaderTranslation(),
squishiness
);
+ if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE
+ && mPanelViewControllerLazy.get().mAnimateBack) {
+ mPanelViewControllerLazy.get().adjustBackAnimationScale(adjustedExpansionFraction);
+ }
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -1113,6 +1150,7 @@ public class QuickSettingsController {
float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
+ mScrimController.setNotificationBottomRadius(radius);
}
if (isQsFragmentCreated()) {
float qsTranslation = 0;
@@ -1505,18 +1543,31 @@ public class QuickSettingsController {
}
private void handleDown(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN
- && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
- mFalsingCollector.onQsDown();
- mShadeLog.logMotionEvent(event, "handleQsDown: down action, QS tracking enabled");
- mTracking = true;
- onExpansionStarted();
- mInitialHeightOnTouch = mExpansionHeight;
- mInitialTouchY = event.getY();
- mInitialTouchX = event.getX();
- // TODO (b/265193930): remove dependency on NPVC
- // If we interrupt an expansion gesture here, make sure to update the state correctly.
- mPanelViewControllerLazy.get().notifyExpandingFinished();
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // When the shade is fully-expanded, an inward swipe from the L/R edge should first
+ // allow the back gesture's animation to preview the shade animation (if enabled).
+ // (swipes starting closer to the center of the screen will not be affected)
+ if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE
+ && mPanelViewControllerLazy.get().mAnimateBack) {
+ updateGestureInsetsCache();
+ if (shouldBackBypassQuickSettings(event.getX())) {
+ return;
+ }
+ }
+ if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
+ mFalsingCollector.onQsDown();
+ mShadeLog.logMotionEvent(event,
+ "handleQsDown: down action, QS tracking enabled");
+ mTracking = true;
+ onExpansionStarted();
+ mInitialHeightOnTouch = mExpansionHeight;
+ mInitialTouchY = event.getY();
+ mInitialTouchX = event.getX();
+ // TODO (b/265193930): remove dependency on NPVC
+ // If we interrupt an expansion gesture here, make sure to update the state
+ // correctly.
+ mPanelViewControllerLazy.get().notifyExpandingFinished();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index e9fac28395ea..1cfb400280fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -39,7 +39,7 @@ public class KeyboardShortcutsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (mIsShortcutListSearchEnabled && Utilities.isTablet(context)) {
+ if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(context)) {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index fc89be2c6670..00d8c421c721 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -28,10 +28,6 @@ class NotifPipelineFlags @Inject constructor(
val featureFlags: FeatureFlags,
val sysPropFlags: FlagResolver,
) {
- init {
- featureFlags.addListener(Flags.DISABLE_FSI) { event -> event.requestNoRestart() }
- }
-
fun isDevLoggingEnabled(): Boolean =
featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
@@ -40,8 +36,6 @@ class NotifPipelineFlags @Inject constructor(
fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE)
- fun disableFsi(): Boolean = featureFlags.isEnabled(Flags.DISABLE_FSI)
-
fun forceDemoteFsi(): Boolean =
sysPropFlags.isEnabled(NotificationFlags.FSI_FORCE_DEMOTE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt
deleted file mode 100644
index b48322822c86..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.RemoteException
-import android.service.dreams.IDreamManager
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
-import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log
-import com.android.systemui.statusbar.phone.CentralSurfaces
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-/**
- * Class that bridges the gap between clean app architecture and existing code. Provides new
- * implementation of StatusBarNotificationActivityStarter launchFullscreenIntent that pipes
- * one-directional data => FsiChromeViewModel => FsiChromeView.
- */
-@SysUISingleton
-class FsiChromeRepo
-@Inject
-constructor(
- private val context: Context,
- private val pm: PackageManager,
- private val keyguardRepo: KeyguardRepository,
- private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
- private val featureFlags: FeatureFlags,
- private val uiBgExecutor: Executor,
- private val dreamManager: IDreamManager,
- private val centralSurfaces: CentralSurfaces
-) : CoreStartable {
-
- companion object {
- private const val classTag = "FsiChromeRepo"
- }
-
- data class FSIInfo(
- val appName: String,
- val appIcon: Drawable,
- val fullscreenIntent: PendingIntent
- )
-
- private val _infoFlow = MutableStateFlow<FSIInfo?>(null)
- val infoFlow: StateFlow<FSIInfo?> = _infoFlow
-
- override fun start() {
- log("$classTag start listening for FSI notifications")
-
- // Listen for FSI launch events for the lifetime of SystemUI.
- launchFullScreenIntentProvider.registerListener { entry -> launchFullscreenIntent(entry) }
- }
-
- fun dismiss() {
- _infoFlow.value = null
- }
-
- fun onFullscreen() {
- // TODO(b/243421660) implement transition from container to fullscreen
- }
-
- fun stopScreenSaver() {
- uiBgExecutor.execute {
- try {
- dreamManager.awaken()
- } catch (e: RemoteException) {
- e.printStackTrace()
- }
- }
- }
-
- fun launchFullscreenIntent(entry: NotificationEntry) {
- if (!featureFlags.isEnabled(Flags.FSI_CHROME)) {
- return
- }
- if (!keyguardRepo.isKeyguardShowing()) {
- return
- }
- stopScreenSaver()
-
- var appName = pm.getApplicationLabel(context.applicationInfo) as String
- val appIcon = pm.getApplicationIcon(context.packageName)
- val fullscreenIntent = entry.sbn.notification.fullScreenIntent
-
- log("FsiChromeRepo launchFullscreenIntent appName=$appName appIcon $appIcon")
- _infoFlow.value = FSIInfo(appName, appIcon, fullscreenIntent)
-
- // If screen is off or we're showing AOD, show lockscreen.
- centralSurfaces.wakeUpForFullScreenIntent()
-
- // Don't show HUN since we're already showing FSI.
- entry.notifyFullScreenIntentLaunched()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt
deleted file mode 100644
index 6e5fcf40440c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-import android.content.Context
-import android.graphics.Color
-import android.graphics.Color.DKGRAY
-import android.graphics.Outline
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewOutlineProvider
-import android.widget.Button
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log
-
-@SysUISingleton
-class FsiChromeView
-@JvmOverloads
-constructor(
- context: Context?,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
- defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
-
- companion object {
- private const val classTag = "FsiChromeView"
- }
-
- lateinit var chromeContainer: LinearLayout
- lateinit var appIconImageView: ImageView
- lateinit var appNameTextView: TextView
- lateinit var dismissButton: Button
- lateinit var fullscreenButton: Button
-
- private val cornerRadius: Float =
- resources.getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
- private val vertPadding: Int =
- resources.getDimensionPixelSize(R.dimen.fsi_chrome_vertical_padding)
- private val sidePadding: Int =
- resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
-
- init {
- log("$classTag init")
- }
-
- override fun onFinishInflate() {
- log("$classTag onFinishInflate")
- super.onFinishInflate()
-
- setBackgroundColor(Color.TRANSPARENT)
- setPadding(
- sidePadding,
- vertPadding,
- sidePadding,
- vertPadding
- ) // Make smaller than fullscreen.
-
- chromeContainer = findViewById(R.id.fsi_chrome)
- chromeContainer.setBackgroundColor(DKGRAY)
-
- appIconImageView = findViewById(R.id.fsi_app_icon)
- appNameTextView = findViewById(R.id.fsi_app_name)
- dismissButton = findViewById(R.id.fsi_dismiss_button)
- fullscreenButton = findViewById(R.id.fsi_fullscreen_button)
-
- outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- outline.setRoundRect(
- /* left */ sidePadding,
- /* top */ vertPadding,
- /* right */ view.width - sidePadding,
- /* bottom */ view.height - vertPadding,
- cornerRadius
- )
- }
- }
- clipToOutline = true
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt
deleted file mode 100644
index 1a3927ba9b06..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-import android.content.Context
-import android.view.LayoutInflater
-import android.view.WindowManager
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log
-import com.android.systemui.statusbar.phone.CentralSurfaces
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-@SysUISingleton
-class FsiChromeViewBinder
-@Inject
-constructor(
- val context: Context,
- val windowManager: WindowManager,
- val viewModelFactory: FsiChromeViewModelFactory,
- val layoutInflater: LayoutInflater,
- val centralSurfaces: CentralSurfaces,
- @Main val mainExecutor: Executor,
- @Application val scope: CoroutineScope,
-) : CoreStartable {
-
- companion object {
- private const val classTag = "FsiChromeViewBinder"
- }
-
- private val fsiChromeView =
- layoutInflater.inflate(R.layout.fsi_chrome_view, null /* root */, false /* attachToRoot */)
- as FsiChromeView
-
- var addedToWindowManager = false
- var cornerRadius: Int = context.resources.getDimensionPixelSize(
- R.dimen.notification_corner_radius)
-
- override fun start() {
- val methodTag = "start"
- log("$classTag $methodTag ")
-
- scope.launch {
- log("$classTag $methodTag launch ")
- viewModelFactory.viewModelFlow.collect { vm -> updateForViewModel(vm) }
- }
- }
-
- private fun updateForViewModel(vm: FsiChromeViewModel?) {
- val methodTag = "updateForViewModel"
-
- if (vm == null) {
- log("$classTag $methodTag viewModel is null, removing from window manager")
-
- if (addedToWindowManager) {
- windowManager.removeView(fsiChromeView)
- addedToWindowManager = false
- }
- return
- }
-
- bindViewModel(vm, windowManager)
-
- if (addedToWindowManager) {
- log("$classTag $methodTag already addedToWindowManager")
- } else {
- windowManager.addView(fsiChromeView, FsiTaskViewConfig.getWmLayoutParams("PackageName"))
- addedToWindowManager = true
- }
- }
-
- private fun bindViewModel(
- vm: FsiChromeViewModel,
- windowManager: WindowManager,
- ) {
- log("$classTag bindViewModel")
-
- fsiChromeView.appIconImageView.setImageDrawable(vm.appIcon)
- fsiChromeView.appNameTextView.text = vm.appName
-
- fsiChromeView.dismissButton.setOnClickListener { vm.onDismiss() }
- fsiChromeView.fullscreenButton.setOnClickListener { vm.onFullscreen() }
-
- vm.taskView.cornerRadius = cornerRadius.toFloat()
- vm.taskView.startActivity(
- vm.fsi,
- FsiTaskViewConfig.getFillInIntent(),
- FsiTaskViewConfig.getActivityOptions(context, windowManager),
- FsiTaskViewConfig.getLaunchBounds(windowManager)
- )
-
- log("$classTag bindViewModel started taskview activity")
- fsiChromeView.addView(vm.taskView)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt
deleted file mode 100644
index 1ca698b6bd58..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-import android.annotation.UiContext
-import android.app.PendingIntent
-import android.content.Context
-import android.graphics.drawable.Drawable
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log
-import com.android.wm.shell.TaskView
-import com.android.wm.shell.TaskViewFactory
-import java.util.Optional
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlin.coroutines.resume
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.suspendCancellableCoroutine
-
-/**
- * Handle view-related data for fullscreen intent container on lockscreen. Wraps FsiChromeRepo,
- * transforms events/state into view-relevant representation for FsiChromeView. Alive for lifetime
- * of SystemUI.
- */
-@SysUISingleton
-class FsiChromeViewModelFactory
-@Inject
-constructor(
- val repo: FsiChromeRepo,
- val taskViewFactory: Optional<TaskViewFactory>,
- @UiContext val context: Context,
- @Main val mainExecutor: Executor,
-) : CoreStartable {
-
- companion object {
- private const val classTag = "FsiChromeViewModelFactory"
- }
-
- val viewModelFlow: Flow<FsiChromeViewModel?> =
- repo.infoFlow.mapLatest { fsiInfo ->
- fsiInfo?.let {
- log("$classTag viewModelFlow got new fsiInfo")
-
- // mapLatest emits null when FSIInfo is null
- FsiChromeViewModel(
- fsiInfo.appName,
- fsiInfo.appIcon,
- createTaskView(),
- fsiInfo.fullscreenIntent,
- repo
- )
- }
- }
-
- override fun start() {
- log("$classTag start")
- }
-
- private suspend fun createTaskView(): TaskView = suspendCancellableCoroutine { k ->
- log("$classTag createTaskView")
-
- taskViewFactory.get().create(context, mainExecutor) { taskView -> k.resume(taskView) }
- }
-}
-
-// Alive for lifetime of FSI.
-data class FsiChromeViewModel(
- val appName: String,
- val appIcon: Drawable,
- val taskView: TaskView,
- val fsi: PendingIntent,
- val repo: FsiChromeRepo
-) {
- companion object {
- private const val classTag = "FsiChromeViewModel"
- }
-
- fun onDismiss() {
- log("$classTag onDismiss")
- repo.dismiss()
- }
- fun onFullscreen() {
- log("$classTag onFullscreen")
- repo.onFullscreen()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt
deleted file mode 100644
index d9e3f8fbf146..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-class FsiDebug {
-
- companion object {
- private const val debugTag = "FsiDebug"
- private const val debug = true
-
- fun log(s: Any) {
- if (!debug) {
- return
- }
- android.util.Log.d(debugTag, "$s")
- }
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt
deleted file mode 100644
index 034ab56d5a65..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.android.systemui.statusbar.notification.fsi
-
-import android.app.ActivityOptions
-import android.content.Context
-import android.content.Intent
-import android.graphics.PixelFormat
-import android.graphics.Rect
-import android.os.Binder
-import android.view.ViewGroup
-import android.view.WindowManager
-
-/**
- * Config for adding the FsiChromeView window to WindowManager and starting the FSI activity.
- */
-class FsiTaskViewConfig {
-
- companion object {
-
- private const val classTag = "FsiTaskViewConfig"
-
- fun getWmLayoutParams(packageName: String): WindowManager.LayoutParams {
- val params: WindowManager.LayoutParams?
- params =
- WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or
- WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER,
- PixelFormat.TRANSLUCENT
- )
- params.setTrustedOverlay()
- params.fitInsetsTypes = 0
- params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
- params.token = Binder()
- params.packageName = packageName
- params.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- params.privateFlags =
- params.privateFlags or WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
- return params
- }
-
- fun getFillInIntent(): Intent {
- val fillInIntent = Intent()
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- // FLAG_ACTIVITY_NEW_TASK is auto-applied because
- // we're starting the FSI activity from a non-Activity context
- return fillInIntent
- }
-
- fun getLaunchBounds(windowManager: WindowManager): Rect {
- // TODO(b/243421660) check this works for non-resizeable activity
- return Rect()
- }
-
- fun getActivityOptions(context: Context, windowManager: WindowManager): ActivityOptions {
- // Custom options so there is no activity transition animation
- val options =
- ActivityOptions.makeCustomAnimation(context, 0 /* enterResId */, 0 /* exitResId */)
-
- options.taskAlwaysOnTop = true
-
- options.pendingIntentLaunchFlags =
- Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
- Intent.FLAG_ACTIVITY_NEW_TASK
-
- options.launchBounds = getLaunchBounds(windowManager)
- return options
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index ae19febadfaa..9001470ad406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -35,10 +35,6 @@ public interface NotificationInterruptStateProvider {
*/
NO_FSI_SHOW_STICKY_HUN(false),
/**
- * Full screen intents are disabled.
- */
- NO_FSI_DISABLED(false),
- /**
* No full screen intent included, so there is nothing to show.
*/
NO_FULL_SCREEN_INTENT(false),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 0163dbef2760..9f45b9d67189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -244,10 +244,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
@Override
public FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry) {
- if (mFlags.disableFsi()) {
- return FullScreenIntentDecision.NO_FSI_DISABLED;
- }
-
if (entry.getSbn().getNotification().fullScreenIntent == null) {
if (entry.isStickyAndNotDemoted()) {
return FullScreenIntentDecision.NO_FSI_SHOW_STICKY_HUN;
@@ -343,9 +339,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
case NO_FSI_SHOW_STICKY_HUN:
mLogger.logNoFullscreen(entry, "Permission denied, show sticky HUN");
return;
- case NO_FSI_DISABLED:
- mLogger.logNoFullscreen(entry, "Disabled");
- return;
case NO_FULL_SCREEN_INTENT:
return;
case NO_FSI_SUPPRESSED_BY_DND:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index f0b221dd4726..0de3246e01fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -344,7 +344,7 @@ class ChannelEditorDialogController @Inject constructor(
or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
-class ChannelEditorDialog(context: Context) : Dialog(context) {
+class ChannelEditorDialog(context: Context, theme: Int) : Dialog(context, theme) {
fun updateDoneButtonText(hasChanges: Boolean) {
findViewById<TextView>(R.id.done_button)?.setText(
if (hasChanges)
@@ -361,7 +361,7 @@ class ChannelEditorDialog(context: Context) : Dialog(context) {
}
fun build(): ChannelEditorDialog {
- return ChannelEditorDialog(context)
+ return ChannelEditorDialog(context, R.style.Theme_SystemUI_Dialog)
}
}
}
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 9f3836105a95..7855cdfeb4c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -374,6 +374,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
@Override
+ public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ Trace.beginSection("BiometricUnlockController#onBiometricDetected");
+ if (mUpdateMonitor.isGoingToSleep()) {
+ Trace.endSection();
+ return;
+ }
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ }
+
+ @Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 8dcfec71b68e..9e62817fcc67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -375,8 +375,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
Runnable endRunnable, Runnable cancelRunnable);
- void animateKeyguardUnoccluding();
-
void startLaunchTransitionTimeout();
boolean hideKeyguardImpl(boolean forceStateChange);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 17fb05547e00..b166446714de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -104,6 +104,8 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
@@ -164,6 +166,7 @@ import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.notetask.NoteTaskController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.OverlayPlugin;
@@ -507,6 +510,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
+ private final boolean mAnimateBack;
private final FragmentService mFragmentService;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final WallpaperController mWallpaperController;
@@ -642,7 +646,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private NotificationActivityStarter mNotificationActivityStarter;
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
private final Optional<Bubbles> mBubblesOptional;
- private final Bubbles.BubbleExpandListener mBubbleExpandListener;
+ private final Lazy<NoteTaskController> mNoteTaskControllerLazy;
private final Optional<StartingSurface> mStartingSurfaceOptional;
private final ActivityIntentHelper mActivityIntentHelper;
@@ -653,6 +657,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final InteractionJankMonitor mJankMonitor;
+ /** Existing callback that handles back gesture invoked for the Shade. */
private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
if (DEBUG) {
Log.d(TAG, "mOnBackInvokedCallback() called");
@@ -660,6 +665,33 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
onBackPressed();
};
+ private boolean shouldBackBeHandled() {
+ return (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
+ && !isBouncerShowingOverDream());
+ }
+
+ /**
+ * New callback that handles back gesture invoked, cancel, progress
+ * and provides feedback via Shade animation.
+ * (enabled via the WM_SHADE_ANIMATE_BACK_GESTURE flag)
+ */
+ private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() {
+ @Override
+ public void onBackInvoked() {
+ onBackPressed();
+ }
+
+ @Override
+ public void onBackProgressed(BackEvent event) {
+ if (shouldBackBeHandled()) {
+ if (mNotificationPanelViewController.canPanelBeCollapsed()) {
+ float fraction = event.getProgress();
+ mNotificationPanelViewController.onBackProgressed(fraction);
+ }
+ }
+ }
+ };
+
/**
* Public constructor for CentralSurfaces.
*
@@ -705,6 +737,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
+ Lazy<NoteTaskController> noteTaskControllerLazy,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
AccessibilityFloatingMenuController accessibilityFloatingMenuController,
@@ -795,6 +828,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
+ mNoteTaskControllerLazy = noteTaskControllerLazy;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
@@ -852,9 +886,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
- mBubbleExpandListener = (isExpanding, key) ->
- mContext.getMainExecutor().execute(this::updateScrimController);
-
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -882,6 +913,17 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
}
+ // Based on teamfood flag, enable predictive back animation for the Shade.
+ mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
+ }
+
+ private void initBubbles(Bubbles bubbles) {
+ final Bubbles.BubbleExpandListener listener = (isExpanding, key) ->
+ mContext.getMainExecutor().execute(() -> {
+ updateScrimController();
+ mNoteTaskControllerLazy.get().onBubbleExpandChanged(isExpanding, key);
+ });
+ bubbles.setExpandListener(listener);
}
@Override
@@ -889,9 +931,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
- if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
- }
+ mBubblesOptional.ifPresent(this::initBubbles);
// Do not restart System UI when the bugreport flag changes.
mFeatureFlags.addListener(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, event -> {
@@ -2554,7 +2594,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+ if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) {
KeyboardShortcutListSearch.dismiss();
} else {
KeyboardShortcuts.dismiss();
@@ -2699,7 +2739,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (viewRootImpl != null) {
viewRootImpl.getOnBackInvokedDispatcher()
.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- mOnBackInvokedCallback);
+ mAnimateBack ? mOnBackAnimationCallback
+ : mOnBackInvokedCallback);
mIsBackCallbackRegistered = true;
if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
}
@@ -2714,7 +2755,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
ViewRootImpl viewRootImpl = getViewRootImpl();
if (viewRootImpl != null) {
viewRootImpl.getOnBackInvokedDispatcher()
- .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ .unregisterOnBackInvokedCallback(
+ mAnimateBack ? mOnBackAnimationCallback
+ : mOnBackInvokedCallback);
mIsBackCallbackRegistered = false;
if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
}
@@ -2991,16 +3034,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
/**
- * Plays the animation when an activity that was occluding Keyguard goes away.
- */
- @Override
- public void animateKeyguardUnoccluding() {
- mNotificationPanelViewController.setExpandedFraction(0f);
- mCommandQueueCallbacks.animateExpandNotificationsPanel();
- mScrimController.setUnocclusionAnimationRunning(true);
- }
-
- /**
* Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
* Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
* because the launched app crashed or something else went wrong.
@@ -3256,9 +3289,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
return true;
}
- if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
- && !isBouncerShowingOverDream()) {
+ if (shouldBackBeHandled()) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
+ // this is the Shade dismiss animation, so make sure QQS closes when it ends.
+ mNotificationPanelViewController.onBackPressed();
mShadeController.animateCollapseShade();
}
return true;
@@ -3905,7 +3939,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
protected void toggleKeyboardShortcuts(int deviceId) {
- if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+ if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) {
KeyboardShortcutListSearch.toggle(mContext, deviceId);
} else {
KeyboardShortcuts.toggle(mContext, deviceId);
@@ -3913,7 +3947,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
protected void dismissKeyboardShortcuts() {
- if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
+ if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) {
KeyboardShortcutListSearch.dismiss();
} else {
KeyboardShortcuts.dismiss();
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 80093a3da325..fb8bf523f625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -138,26 +138,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private boolean mTransitioningToFullShade;
/**
- * Is there currently an unocclusion animation running. Used to avoid bright flickers
- * of the notification scrim.
- */
- private boolean mUnOcclusionAnimationRunning;
-
- /**
* The percentage of the bouncer which is hidden. If 1, the bouncer is completely hidden. If
* 0, the bouncer is visible.
*/
@FloatRange(from = 0, to = 1)
private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN;
- /**
- * Set whether an unocclusion animation is currently running on the notification panel. Used
- * to avoid bright flickers of the notification scrim.
- */
- public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) {
- mUnOcclusionAnimationRunning = unocclusionAnimationRunning;
- }
-
@IntDef(prefix = {"VISIBILITY_"}, value = {
TRANSPARENT,
SEMI_TRANSPARENT,
@@ -359,9 +345,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- /**
- * Sets corner radius of scrims.
- */
+ // TODO(b/270984686) recompute scrim height accurately, based on shade contents.
+ /** Set corner radius of the bottom edge of the Notification scrim. */
+ public void setNotificationBottomRadius(float radius) {
+ if (mNotificationsScrim == null) {
+ return;
+ }
+ mNotificationsScrim.setBottomEdgeRadius(radius);
+ }
+
+ /** Sets corner radius of scrims. */
public void setScrimCornerRadius(int radius) {
if (mScrimBehind == null || mNotificationsScrim == null) {
return;
@@ -525,6 +518,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
scheduleUpdate();
}
+ /** This is used by the predictive back gesture animation to scale the Shade. */
+ public void applyBackScaling(float scale) {
+ mNotificationsScrim.setScaleX(scale);
+ mNotificationsScrim.setScaleY(scale);
+ }
+
public void onTrackingStarted() {
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
@@ -532,10 +531,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- public void onExpandingFinished() {
- setUnocclusionAnimationRunning(false);
- }
-
@VisibleForTesting
protected void onHideWallpaperTimeout() {
if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
@@ -875,13 +870,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (mKeyguardOccluded || hideNotificationScrim) {
mNotificationsAlpha = 0;
}
- if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
- // We're unoccluding the keyguard and don't want to have a bright flash.
- mNotificationsAlpha = ScrimState.KEYGUARD.getNotifAlpha();
- mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
- mBehindAlpha = ScrimState.KEYGUARD.getBehindAlpha();
- mBehindTint = ScrimState.KEYGUARD.getBehindTint();
- }
}
if (mState != ScrimState.UNLOCKED) {
mAnimatingPanelExpansionOnUnlock = false;
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 a127139fcc69..66f5b6508494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -281,7 +281,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private float mQsExpansion;
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
private boolean mIsModernAlternateBouncerEnabled;
- private boolean mIsUnoccludeTransitionFlagEnabled;
private boolean mIsBackAnimationEnabled;
private OnDismissAction mAfterKeyguardGoneAction;
@@ -361,7 +360,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER);
mAlternateBouncerInteractor = alternateBouncerInteractor;
- mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
mIsBackAnimationEnabled =
featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
}
@@ -880,11 +878,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (!mIsUnoccludeTransitionFlagEnabled) {
- if (animate && !isOccluded && isShowing && !primaryBouncerIsShowing()) {
- mCentralSurfaces.animateKeyguardUnoccluding();
- }
- }
}
@Override
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 3471a4656637..726b2344309f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -561,10 +561,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mLogger.logFullScreenIntentSuppressedByVR(entry);
return;
}
- if (mFeatureFlags.isEnabled(Flags.FSI_CHROME)) {
- // FsiChromeRepo runs its own implementation of launchFullScreenIntent
- return;
- }
// Stop screensaver if the notification has a fullscreen intent.
// (like an incoming phone call)
mUiBgExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 462504e82199..4e27ce6721d7 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -82,6 +82,8 @@ constructor(
fun startListener() {
handler.post {
if (hasStarted) return@post
+ logDebug { "Listener has started." }
+
hasStarted = true
isInUsiSession =
inputManager.hasInputDevice {
@@ -116,6 +118,10 @@ constructor(
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
+ logDebug {
+ "Stylus InputDevice added: $deviceId ${device.name}, " +
+ "External: ${device.isExternal}"
+ }
if (!device.isExternal) {
registerBatteryListener(deviceId)
@@ -137,6 +143,7 @@ constructor(
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
+ logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" }
val currAddress: String? = device.bluetoothAddress
val prevAddress: String? = inputDeviceAddressMap[deviceId]
@@ -157,6 +164,8 @@ constructor(
if (!hasStarted) return
if (!inputDeviceAddressMap.contains(deviceId)) return
+ logDebug { "Stylus InputDevice removed: $deviceId" }
+
unregisterBatteryListener(deviceId)
val btAddress: String? = inputDeviceAddressMap[deviceId]
@@ -180,6 +189,11 @@ constructor(
val isCharging = String(value) == "true"
+ logDebug {
+ "Charging state metadata changed for device $inputDeviceId " +
+ "${device.address}: $isCharging"
+ }
+
executeStylusBatteryCallbacks { cb ->
cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging)
}
@@ -194,13 +208,10 @@ constructor(
handler.post {
if (!hasStarted) return@post
- if (DEBUG) {
- Log.d(
- TAG,
- "onBatteryStateChanged for $deviceId. " +
- "batteryState present: ${batteryState.isPresent}, " +
- "capacity: ${batteryState.capacity}"
- )
+ logDebug {
+ "Battery state changed for $deviceId. " +
+ "batteryState present: ${batteryState.isPresent}, " +
+ "capacity: ${batteryState.capacity}"
}
val batteryStateValid = isBatteryStateValid(batteryState)
@@ -216,7 +227,7 @@ constructor(
}
private fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
- trackAndLogBluetoothSession(deviceId, true)
+ trackAndLogBluetoothSession(deviceId, btAddress, true)
val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return
try {
bluetoothAdapter.addOnMetadataChangedListener(device, executor, this)
@@ -226,7 +237,7 @@ constructor(
}
private fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {
- trackAndLogBluetoothSession(deviceId, false)
+ trackAndLogBluetoothSession(deviceId, btAddress, false)
val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return
try {
bluetoothAdapter.removeOnMetadataChangedListener(device, this)
@@ -245,6 +256,7 @@ constructor(
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
if (InputSettings.isStylusEverUsed(context)) return
+ logDebug { "Stylus used for the first time." }
InputSettings.setStylusEverUsed(context, true)
executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
}
@@ -259,12 +271,7 @@ constructor(
// TODO(b/268618918) handle cases where an invalid battery callback from a previous stylus
// is sent after the actual valid callback
if (batteryStateValid && usiSessionId == null) {
- if (DEBUG) {
- Log.d(
- TAG,
- "USI battery newly present, entering new USI session. Device ID: $deviceId"
- )
- }
+ logDebug { "USI battery newly present, entering new USI session: $deviceId" }
usiSessionId = instanceIdSequence.newInstanceId()
uiEventLogger.logWithInstanceId(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
@@ -273,9 +280,7 @@ constructor(
usiSessionId
)
} else if (!batteryStateValid && usiSessionId != null) {
- if (DEBUG) {
- Log.d(TAG, "USI battery newly absent, exiting USI session Device ID: $deviceId")
- }
+ logDebug { "USI battery newly absent, exiting USI session: $deviceId" }
uiEventLogger.logWithInstanceId(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
0,
@@ -286,8 +291,17 @@ constructor(
}
}
- private fun trackAndLogBluetoothSession(deviceId: Int, bluetoothConnected: Boolean) {
- if (bluetoothConnected) {
+ private fun trackAndLogBluetoothSession(
+ deviceId: Int,
+ btAddress: String,
+ btConnected: Boolean
+ ) {
+ logDebug {
+ "Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" +
+ " $deviceId $btAddress"
+ }
+
+ if (btConnected) {
inputDeviceBtSessionIdMap[deviceId] = instanceIdSequence.newInstanceId()
uiEventLogger.logWithInstanceId(
StylusUiEvent.BLUETOOTH_STYLUS_CONNECTED,
@@ -383,7 +397,13 @@ constructor(
}
companion object {
- private val TAG = StylusManager::class.simpleName.orEmpty()
- private val DEBUG = false
+ val TAG = StylusManager::class.simpleName.orEmpty()
+ const val DEBUG = false
+ }
+}
+
+private inline fun logDebug(message: () -> String) {
+ if (StylusManager.DEBUG) {
+ Log.d(StylusManager.TAG, message())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index dfad15d68375..71449145d668 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -26,7 +26,6 @@ import android.testing.AndroidTestingRunner;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -61,8 +60,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
@Mock
DozeParameters mDozeParameters;
@Mock
- FeatureFlags mFeatureFlags;
- @Mock
ScreenOffAnimationController mScreenOffAnimationController;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -83,7 +80,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mConfigurationController,
mDozeParameters,
- mFeatureFlags,
mScreenOffAnimationController,
mKeyguardLogger);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 395eb8fefb8d..f5107681635c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -20,6 +20,8 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN;
+import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -29,7 +31,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
-import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
@@ -81,6 +82,7 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -632,20 +634,48 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() {
- // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
- // will trigger updateBiometricListeningState();
- clearInvocations(mFingerprintManager);
- mKeyguardUpdateMonitor.resetBiometricListeningState();
-
- when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
- mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
- mTestableLooper.processAllMessages();
+ givenDetectFingerprintWithClearingFingerprintManagerInvocations();
verifyFingerprintAuthenticateNeverCalled();
verifyFingerprintDetectCall();
}
@Test
+ public void whenDetectFingerprint_biometricDetectCallback() {
+ ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
+ ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);
+
+ givenDetectFingerprintWithClearingFingerprintManagerInvocations();
+ verify(mFingerprintManager).detectFingerprint(
+ any(), fpDetectCallbackCaptor.capture(), any());
+ fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true);
+
+ // THEN verify keyguardUpdateMonitorCallback receives a detect callback
+ // and NO authenticate callbacks
+ verify(mTestCallback).onBiometricDetected(
+ eq(0), eq(BiometricSourceType.FINGERPRINT), eq(true));
+ verify(mTestCallback, never()).onBiometricAuthenticated(
+ anyInt(), any(), anyBoolean());
+ }
+
+ @Test
+ public void whenDetectFace_biometricDetectCallback() {
+ ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor =
+ ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class);
+
+ givenDetectFace();
+ verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any());
+ faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false);
+
+ // THEN verify keyguardUpdateMonitorCallback receives a detect callback
+ // and NO authenticate callbacks
+ verify(mTestCallback).onBiometricDetected(
+ eq(0), eq(BiometricSourceType.FACE), eq(false));
+ verify(mTestCallback, never()).onBiometricAuthenticated(
+ anyInt(), any(), anyBoolean());
+ }
+
+ @Test
public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
// GIVEN unlocking with biometric is allowed
strongAuthNotRequired();
@@ -673,7 +703,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
strongAuthNotRequired();
// WHEN fingerprint is locked out
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
// THEN unlocking with face is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -696,7 +726,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
strongAuthNotRequired();
// WHEN fingerprint is locked out
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
// THEN unlocking with fingerprint is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -720,7 +750,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
// WHEN fingerprint is locked out
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
// THEN user is NOT considered as "having trust" and bouncer cannot be skipped
Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
@@ -780,11 +810,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() {
- // GIVEN mocked keyguardUpdateMonitorCallback
- KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback =
- mock(KeyguardUpdateMonitorCallback.class);
- mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);
-
// GIVEN bypass is enabled, face detection is supported
lockscreenBypassIsAllowed();
supportsFaceDetection();
@@ -803,22 +828,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verifyFaceAuthenticateNeverCalled();
// THEN biometric help message sent to callback
- verify(keyguardUpdateMonitorCallback).onBiometricHelp(
+ verify(mTestCallback).onBiometricHelp(
eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE));
}
@Test
public void faceDetect_whenStrongAuthRequiredAndBypass() {
- // GIVEN bypass is enabled, face detection is supported and strong auth is required
- lockscreenBypassIsAllowed();
- supportsFaceDetection();
- strongAuthRequiredEncrypted();
- keyguardIsVisible();
- // fingerprint is NOT running, UDFPS is NOT supported
-
- // WHEN the device wakes up
- mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
- mTestableLooper.processAllMessages();
+ givenDetectFace();
// FACE detect is triggered, not authenticate
verifyFaceDetectCall();
@@ -1152,8 +1168,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// Fingerprint should be cancelled on lockout if going to lockout state, else
// restarted if it's not
assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
- .isEqualTo(fpLocked
- ? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING);
+ .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
}
@Test
@@ -1612,7 +1627,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -2465,7 +2480,69 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
eq(false));
}
+ @Test
+ public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() {
+ ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor =
+ ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class);
+ verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture());
+
+ // GIVEN device is locked out
+ fingerprintErrorTemporaryLockedOut();
+
+ // GIVEN FP detection is running
+ givenDetectFingerprintWithClearingFingerprintManagerInvocations();
+ verifyFingerprintDetectCall();
+ verifyFingerprintAuthenticateNeverCalled();
+
+ // WHEN temporary lockout resets
+ fpLockoutResetCallbackCaptor.getValue().onLockoutReset(0);
+ mTestableLooper.processAllMessages();
+
+ // THEN fingerprint detect state should cancel & then restart (for authenticate call)
+ assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
+ .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+ }
+
+ @Test
+ public void faceAuthenticateOptions_bouncerAuthenticateReason() {
+ // GIVEN the bouncer is fully visible
+ bouncerFullyVisible();
+
+ // WHEN authenticate is called
+ ArgumentCaptor<FaceAuthenticateOptions> captor =
+ ArgumentCaptor.forClass(FaceAuthenticateOptions.class);
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture());
+
+ // THEN the authenticate reason is attributed to the bouncer
+ assertThat(captor.getValue().getAuthenticateReason())
+ .isEqualTo(AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN);
+ }
+
+ @Test
+ public void faceAuthenticateOptions_wakingUpAuthenticateReason_powerButtonWakeReason() {
+ // GIVEN keyguard is visible
+ keyguardIsVisible();
+
+ // WHEN device wakes up from the power button
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+
+ // THEN face auth is triggered
+ ArgumentCaptor<FaceAuthenticateOptions> captor =
+ ArgumentCaptor.forClass(FaceAuthenticateOptions.class);
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture());
+
+ // THEN the authenticate reason is attributed to the waking
+ assertThat(captor.getValue().getAuthenticateReason())
+ .isEqualTo(AUTHENTICATE_REASON_STARTED_WAKING_UP);
+
+ // THEN the wake reason is attributed to the power button
+ assertThat(captor.getValue().getWakeReason())
+ .isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON);
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
+ verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyInt(), anyInt());
}
@@ -2484,11 +2561,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void verifyFaceAuthenticateNeverCalled() {
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
}
private void verifyFaceAuthenticateCall() {
- verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), any());
}
private void verifyFaceDetectNeverCalled() {
@@ -2568,7 +2646,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.setSwitchingUser(true);
}
- private void fingerprintErrorLockedOut() {
+ private void fingerprintErrorTemporaryLockedOut() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
@@ -2596,7 +2674,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
any(),
mAuthenticationCallbackCaptor.capture(),
any(),
- anyInt());
+ any());
mAuthenticationCallbackCaptor.getValue()
.onAuthenticationSucceeded(
new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
@@ -2707,6 +2785,30 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
receiver.setPendingResult(pendingResult);
}
+ private void givenDetectFingerprintWithClearingFingerprintManagerInvocations() {
+ // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+ // will trigger updateBiometricListeningState();
+ clearInvocations(mFingerprintManager);
+ mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+ }
+
+ private void givenDetectFace() {
+ // GIVEN bypass is enabled, face detection is supported and strong auth is required
+ lockscreenBypassIsAllowed();
+ supportsFaceDetection();
+ strongAuthRequiredEncrypted();
+ keyguardIsVisible();
+ // fingerprint is NOT running, UDFPS is NOT supported
+
+ // WHEN the device wakes up
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ }
+
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
new file mode 100644
index 000000000000..820329119f8d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.devicepolicy
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class DevicePolicyManagerExtTest : SysuiTestCase() {
+
+ @Mock lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var userTracker: UserTracker
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(userTracker.userId).thenReturn(CURRENT_USER_ID)
+ }
+
+ // region areKeyguardShortcutsDisabled
+ @Test
+ fun areKeyguardShortcutsDisabled_noDisabledKeyguardFeature_shouldReturnFalse() {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE)
+
+ assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID))
+ .isFalse()
+ }
+
+ @Test
+ fun areKeyguardShortcutsDisabled_otherDisabledKeyguardFeatures_shouldReturnFalse() {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_CAMERA or KEYGUARD_DISABLE_FACE)
+
+ assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID))
+ .isFalse()
+ }
+
+ @Test
+ fun areKeyguardShortcutsDisabled_disabledShortcutsKeyguardFeature_shouldReturnTrue() {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_SHORTCUTS_ALL)
+
+ assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID))
+ .isTrue()
+ }
+
+ @Test
+ fun areKeyguardShortcutsDisabled_disabledAllKeyguardFeatures_shouldReturnTrue() {
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_ALL)
+
+ assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID))
+ .isTrue()
+ }
+ // endregion
+
+ private companion object {
+ const val CURRENT_USER_ID = 123
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 7c20e3c9baff..c93e677071cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -67,7 +66,6 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -136,7 +134,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock SysuiColorExtractor mColorExtractor;
private @Mock AuthController mAuthController;
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
- private @Mock FeatureFlags mFeatureFlags;
private @Mock ShadeWindowLogger mShadeWindowLogger;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -545,7 +542,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mScreenOnCoordinator,
mInteractionJankMonitor,
mDreamOverlayStateController,
- mFeatureFlags,
() -> mShadeController,
() -> mNotificationShadeWindowController,
() -> mActivityLaunchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 2212bbda8021..89405c109224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -141,8 +141,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@Test
public void testCreateNavigationBarsIncludeDefaultTrue() {
- // Tablets may be using taskbar and the logic is different
- mNavigationBarController.mIsTablet = false;
+ // Large screens may be using taskbar and the logic is different
+ mNavigationBarController.mIsLargeScreen = false;
doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
mNavigationBarController.createNavigationBars(true, null);
@@ -290,7 +290,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@Test
public void testConfigurationChange_taskbarNotInitialized() {
Configuration configuration = mContext.getResources().getConfiguration();
- when(Utilities.isTablet(any())).thenReturn(true);
+ when(Utilities.isLargeScreen(any())).thenReturn(true);
mNavigationBarController.onConfigChanged(configuration);
verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
}
@@ -298,7 +298,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@Test
public void testConfigurationChange_taskbarInitialized() {
Configuration configuration = mContext.getResources().getConfiguration();
- when(Utilities.isTablet(any())).thenReturn(true);
+ when(Utilities.isLargeScreen(any())).thenReturn(true);
when(mTaskbarDelegate.isInitialized()).thenReturn(true);
mNavigationBarController.onConfigChanged(configuration);
verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 39c4e06ff0bb..b872389ce2fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -16,40 +16,37 @@
package com.android.systemui.notetask
import android.app.KeyguardManager
+import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.UserManager
-import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskController.Companion.INTENT_EXTRA_USE_STYLUS_MODE
-import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
-import com.android.systemui.notetask.NoteTaskInfoResolver.NoteTaskInfo
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
-/**
- * Tests for [NoteTaskController].
- *
- * Build/Install/Run:
- * - atest SystemUITests:NoteTaskControllerTest
- */
+/** atest SystemUITests:NoteTaskControllerTest */
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class NoteTaskControllerTest : SysuiTestCase() {
@@ -58,97 +55,204 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
@Mock lateinit var packageManager: PackageManager
@Mock lateinit var resolver: NoteTaskInfoResolver
@Mock lateinit var bubbles: Bubbles
- @Mock lateinit var optionalBubbles: Optional<Bubbles>
@Mock lateinit var keyguardManager: KeyguardManager
- @Mock lateinit var optionalKeyguardManager: Optional<KeyguardManager>
- @Mock lateinit var optionalUserManager: Optional<UserManager>
@Mock lateinit var userManager: UserManager
- @Mock lateinit var uiEventLogger: UiEventLogger
+ @Mock lateinit var eventLogger: NoteTaskEventLogger
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+
+ private val noteTaskInfo = NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(context.packageManager).thenReturn(packageManager)
- whenever(resolver.resolveInfo()).thenReturn(NoteTaskInfo(NOTES_PACKAGE_NAME, NOTES_UID))
- whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
- whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
- whenever(optionalUserManager.orElse(null)).thenReturn(userManager)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo)
whenever(userManager.isUserUnlocked).thenReturn(true)
+ whenever(
+ devicePolicyManager.getKeyguardDisabledFeatures(
+ /* admin= */ eq(null),
+ /* userHandle= */ anyInt()
+ )
+ )
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE)
}
- private fun createNoteTaskController(isEnabled: Boolean = true): NoteTaskController {
- return NoteTaskController(
+ private fun createNoteTaskController(
+ isEnabled: Boolean = true,
+ bubbles: Bubbles? = this.bubbles,
+ keyguardManager: KeyguardManager? = this.keyguardManager,
+ userManager: UserManager? = this.userManager,
+ ): NoteTaskController =
+ NoteTaskController(
context = context,
resolver = resolver,
- optionalBubbles = optionalBubbles,
- optionalKeyguardManager = optionalKeyguardManager,
- optionalUserManager = optionalUserManager,
+ eventLogger = eventLogger,
+ optionalBubbles = Optional.ofNullable(bubbles),
+ optionalUserManager = Optional.ofNullable(userManager),
+ optionalKeyguardManager = Optional.ofNullable(keyguardManager),
isEnabled = isEnabled,
- uiEventLogger = uiEventLogger,
+ devicePolicyManager = devicePolicyManager,
+ userTracker = userTracker,
)
+
+ // region onBubbleExpandChanged
+ @Test
+ fun onBubbleExpandChanged_expanding_logNoteTaskOpened() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+
+ createNoteTaskController()
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = true,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ verify(eventLogger).logNoteTaskOpened(expectedInfo)
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
}
- // region showNoteTask
@Test
- fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
createNoteTaskController()
- .showNoteTask(
- isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE,
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = false,
+ key = Bubble.KEY_APP_BUBBLE,
)
- val intentCaptor = argumentCaptor<Intent>()
- verify(context).startActivity(capture(intentCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
- assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
- }
- verifyZeroInteractions(bubbles)
- verify(uiEventLogger)
- .log(
- ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE,
- NOTES_UID,
- NOTES_PACKAGE_NAME
+ verify(eventLogger).logNoteTaskClosed(expectedInfo)
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
+ }
+
+ @Test
+ fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
+
+ createNoteTaskController()
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = true,
+ key = Bubble.KEY_APP_BUBBLE,
)
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
}
@Test
- fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesAndLogUiEvent() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+ fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
createNoteTaskController()
- .showNoteTask(
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = false,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ }
+
+ @Test
+ fun onBubbleExpandChanged_expandingAndInMultiWindowMode_doNothing() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
+
+ createNoteTaskController()
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = true,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
+ }
+
+ @Test
+ fun onBubbleExpandChanged_notExpandingAndInMultiWindowMode_doNothing() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
+
+ createNoteTaskController()
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = false,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ }
+
+ @Test
+ fun onBubbleExpandChanged_notKeyAppBubble_shouldDoNothing() {
+ createNoteTaskController()
+ .onBubbleExpandChanged(
+ isExpanding = true,
+ key = "any other key",
+ )
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ }
+
+ @Test
+ fun onBubbleExpandChanged_flagDisabled_shouldDoNothing() {
+ createNoteTaskController(isEnabled = false)
+ .onBubbleExpandChanged(
+ isExpanding = true,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ }
+ // endregion
+
+ // region showNoteTask
+ @Test
+ fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() {
+ val expectedInfo =
+ noteTaskInfo.copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+ isKeyguardLocked = true,
+ )
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+
+ createNoteTaskController()
+ .showNoteTask(
+ entryPoint = expectedInfo.entryPoint!!,
+ isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
- verifyZeroInteractions(context)
val intentCaptor = argumentCaptor<Intent>()
- verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
+ verify(context).startActivity(capture(intentCaptor))
intentCaptor.value.let { intent ->
assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
}
- verify(uiEventLogger)
- .log(
- ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
- NOTES_UID,
- NOTES_PACKAGE_NAME
- )
+ verify(eventLogger).logNoteTaskOpened(expectedInfo)
+ verifyZeroInteractions(bubbles)
}
@Test
- fun showNoteTask_keyguardIsUnlocked_uiEventIsNull_shouldStartBubblesWithoutLoggingUiEvent() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+ fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() {
+ val expectedInfo =
+ noteTaskInfo.copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isInMultiWindowMode = false,
+ isKeyguardLocked = false,
+ )
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null)
+ createNoteTaskController()
+ .showNoteTask(
+ entryPoint = expectedInfo.entryPoint!!,
+ isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
+ )
verifyZeroInteractions(context)
val intentCaptor = argumentCaptor<Intent>()
@@ -159,17 +263,24 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
}
- verifyZeroInteractions(uiEventLogger)
+ verifyZeroInteractions(eventLogger)
}
@Test
fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+ val expectedInfo =
+ noteTaskInfo.copy(
+ entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
+ isInMultiWindowMode = true,
+ isKeyguardLocked = false,
+ )
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController()
.showNoteTask(
- isInMultiWindowMode = true,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT,
+ entryPoint = expectedInfo.entryPoint!!,
+ isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
)
val intentCaptor = argumentCaptor<Intent>()
@@ -180,69 +291,65 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
}
+ verify(eventLogger).logNoteTaskOpened(expectedInfo)
verifyZeroInteractions(bubbles)
- verify(uiEventLogger)
- .log(ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT, NOTES_UID, NOTES_PACKAGE_NAME)
}
@Test
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
- whenever(optionalBubbles.orElse(null)).thenReturn(null)
-
- createNoteTaskController()
+ createNoteTaskController(bubbles = null)
.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
)
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() {
- whenever(optionalKeyguardManager.orElse(null)).thenReturn(null)
-
- createNoteTaskController()
+ createNoteTaskController(keyguardManager = null)
.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
)
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
fun showNoteTask_userManagerIsNull_shouldDoNothing() {
- whenever(optionalUserManager.orElse(null)).thenReturn(null)
-
- createNoteTaskController()
+ createNoteTaskController(userManager = null)
.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
)
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
- whenever(resolver.resolveInfo()).thenReturn(null)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
createNoteTaskController()
.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
)
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
fun showNoteTask_flagDisabled_shouldDoNothing() {
createNoteTaskController(isEnabled = false)
- .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
+ .showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isInMultiWindowMode = false,
+ )
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
@@ -251,11 +358,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
createNoteTaskController()
.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
)
- verifyZeroInteractions(context, bubbles, uiEventLogger)
+ verifyZeroInteractions(context, bubbles, eventLogger)
}
// endregion
@@ -291,6 +398,102 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
}
// endregion
+ // region keyguard policy
+ @Test
+ fun showNoteTask_keyguardLocked_keyguardDisableShortcutsAll_shouldDoNothing() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ whenever(
+ devicePolicyManager.getKeyguardDisabledFeatures(
+ /* admin= */ eq(null),
+ /* userHandle= */ anyInt()
+ )
+ )
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
+
+ createNoteTaskController()
+ .showNoteTask(
+ isInMultiWindowMode = false,
+ entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
+ )
+
+ verifyZeroInteractions(context, bubbles, eventLogger)
+ }
+
+ @Test
+ fun showNoteTask_keyguardLocked_keyguardDisableFeaturesAll_shouldDoNothing() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ whenever(
+ devicePolicyManager.getKeyguardDisabledFeatures(
+ /* admin= */ eq(null),
+ /* userHandle= */ anyInt()
+ )
+ )
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+
+ createNoteTaskController()
+ .showNoteTask(
+ isInMultiWindowMode = false,
+ entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
+ )
+
+ verifyZeroInteractions(context, bubbles, eventLogger)
+ }
+
+ @Test
+ fun showNoteTask_keyguardUnlocked_keyguardDisableShortcutsAll_shouldStartBubble() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+ whenever(
+ devicePolicyManager.getKeyguardDisabledFeatures(
+ /* admin= */ eq(null),
+ /* userHandle= */ anyInt()
+ )
+ )
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
+
+ createNoteTaskController()
+ .showNoteTask(
+ isInMultiWindowMode = false,
+ entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
+ )
+
+ val intentCaptor = argumentCaptor<Intent>()
+ verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+ assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+ assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+ assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+ }
+ }
+
+ @Test
+ fun showNoteTask_keyguardUnlocked_keyguardDisableFeaturesAll_shouldStartBubble() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+ whenever(
+ devicePolicyManager.getKeyguardDisabledFeatures(
+ /* admin= */ eq(null),
+ /* userHandle= */ anyInt()
+ )
+ )
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+
+ createNoteTaskController()
+ .showNoteTask(
+ isInMultiWindowMode = false,
+ entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
+ )
+
+ val intentCaptor = argumentCaptor<Intent>()
+ verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+ assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+ assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+ assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+ }
+ }
+ // endregion
+
private companion object {
const val NOTES_PACKAGE_NAME = "com.android.note.app"
const val NOTES_UID = 123456
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
new file mode 100644
index 000000000000..a4df346776a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+/** atest SystemUITests:MonitoringNoteTaskEventListenerTest */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskEventLoggerTest : SysuiTestCase() {
+
+ @Mock lateinit var uiEventLogger: UiEventLogger
+
+ private fun createNoteTaskEventLogger(): NoteTaskEventLogger =
+ NoteTaskEventLogger(uiEventLogger)
+
+ private fun createNoteTaskInfo(): NoteTaskInfo =
+ NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ // region logNoteTaskOpened
+ @Test
+ fun logNoteTaskOpened_entryPointWidgetPickerShortcut_noteOpenedViaShortcut() {
+ val info = createNoteTaskInfo().copy(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ val expected = NOTE_OPENED_VIA_SHORTCUT
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun onNoteTaskBubbleExpanded_entryPointQuickAffordance_noteOpenedViaQuickAffordance() {
+ val info = createNoteTaskInfo().copy(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ val expected = NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun onNoteTaskBubbleExpanded_entryPointTailButtonAndIsKeyguardUnlocked_noteOpenedViaTailButtonUnlocked() { // ktlint-disable max-line-length
+ val info =
+ createNoteTaskInfo()
+ .copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isKeyguardLocked = false,
+ )
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ val expected = NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun onNoteTaskBubbleExpanded_entryPointTailButtonAndIsKeyguardLocked_noteOpenedViaTailButtonLocked() { // ktlint-disable max-line-length
+ val info =
+ createNoteTaskInfo()
+ .copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isKeyguardLocked = true,
+ )
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ val expected = NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun onNoteTaskBubbleExpanded_noEntryPoint_noLog() {
+ val info = createNoteTaskInfo().copy(entryPoint = null)
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ verifyNoMoreInteractions(uiEventLogger)
+ }
+ // endregion
+
+ // region logNoteTaskClosed
+ @Test
+ fun logNoteTaskClosed_entryPointTailButton_noteClosedViaTailButtonUnlocked() {
+ val info =
+ createNoteTaskInfo()
+ .copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isKeyguardLocked = false,
+ )
+
+ createNoteTaskEventLogger().logNoteTaskClosed(info)
+
+ val expected = NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun logNoteTaskClosed_entryPointTailButtonAndKeyguardLocked_noteClosedViaTailButtonLocked() {
+ val info =
+ createNoteTaskInfo()
+ .copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isKeyguardLocked = true,
+ )
+
+ createNoteTaskEventLogger().logNoteTaskClosed(info)
+
+ val expected = NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ verify(uiEventLogger).log(expected, info.uid, info.packageName)
+ }
+
+ @Test
+ fun logNoteTaskClosed_noEntryPoint_noLog() {
+ val info = createNoteTaskInfo().copy(entryPoint = null)
+
+ createNoteTaskEventLogger().logNoteTaskOpened(info)
+
+ verifyNoMoreInteractions(uiEventLogger)
+ }
+ // endregion
+
+ private companion object {
+ const val NOTES_PACKAGE_NAME = "com.android.note.app"
+ const val NOTES_UID = 123456
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
new file mode 100644
index 000000000000..7e975b6732a5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.notetask
+
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** atest SystemUITests:NoteTaskInfoTest */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskInfoTest : SysuiTestCase() {
+
+ private fun createNoteTaskInfo(): NoteTaskInfo =
+ NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+
+ @Test
+ fun launchMode_notInMultiWindowModeAndKeyguardUnlocked_launchModeAppBubble() {
+ val underTest =
+ createNoteTaskInfo()
+ .copy(
+ isKeyguardLocked = false,
+ isInMultiWindowMode = false,
+ )
+
+ assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
+ }
+
+ @Test
+ fun launchMode_inMultiWindowMode_launchModeActivity() {
+ val underTest =
+ createNoteTaskInfo()
+ .copy(
+ isKeyguardLocked = false,
+ isInMultiWindowMode = true,
+ )
+
+ assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
+ }
+
+ @Test
+ fun launchMode_keyguardLocked_launchModeActivity() {
+ val underTest =
+ createNoteTaskInfo()
+ .copy(
+ isKeyguardLocked = true,
+ isInMultiWindowMode = false,
+ )
+
+ assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
+ }
+
+ private companion object {
+ const val NOTES_PACKAGE_NAME = "com.android.note.app"
+ const val NOTES_UID = 123456
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 53720ffdff94..a8eab50e0798 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -15,15 +15,11 @@
*/
package com.android.systemui.notetask
-import android.app.KeyguardManager
import android.test.suitebuilder.annotation.SmallTest
import android.view.KeyEvent
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
-import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import org.junit.Before
@@ -48,27 +44,22 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
@Mock lateinit var commandQueue: CommandQueue
@Mock lateinit var bubbles: Bubbles
- @Mock lateinit var optionalBubbles: Optional<Bubbles>
@Mock lateinit var noteTaskController: NoteTaskController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- whenever(optionalBubbles.isPresent).thenReturn(true)
- whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
}
private fun createNoteTaskInitializer(
isEnabled: Boolean = true,
- optionalKeyguardManager: Optional<KeyguardManager> = Optional.empty(),
+ bubbles: Bubbles? = this.bubbles,
): NoteTaskInitializer {
return NoteTaskInitializer(
- optionalBubbles = optionalBubbles,
- noteTaskController = noteTaskController,
+ controller = noteTaskController,
commandQueue = commandQueue,
+ optionalBubbles = Optional.ofNullable(bubbles),
isEnabled = isEnabled,
- optionalKeyguardManager = optionalKeyguardManager,
)
}
@@ -89,9 +80,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
@Test
fun initialize_bubblesNotPresent_shouldDoNothing() {
- whenever(optionalBubbles.isPresent).thenReturn(false)
-
- createNoteTaskInitializer().initialize()
+ createNoteTaskInitializer(bubbles = null).initialize()
verify(commandQueue, never()).addCallback(any())
}
@@ -113,37 +102,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
// region handleSystemKey
@Test
- fun handleSystemKey_receiveValidSystemKey_keyguardNotLocked_shouldShowNoteTaskWithUnlocked() {
- val keyguardManager =
- mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(false) }
- createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager))
- .callbacks
- .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
-
- verify(noteTaskController)
- .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
- }
-
- @Test
- fun handleSystemKey_receiveValidSystemKey_keyguardLocked_shouldShowNoteTaskWithLocked() {
- val keyguardManager =
- mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(true) }
- createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager))
- .callbacks
- .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
-
- verify(noteTaskController)
- .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED)
- }
-
- @Test
- fun handleSystemKey_receiveValidSystemKey_nullKeyguardManager_shouldShowNoteTaskWithUnlocked() {
- createNoteTaskInitializer(optionalKeyguardManager = Optional.empty())
+ fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
+ createNoteTaskInitializer()
.callbacks
.handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
- verify(noteTaskController)
- .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
+ verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index cdc683f8f8f8..e57d0d943aab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -27,7 +27,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.notetask.NoteTaskController
-import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
+import com.android.systemui.notetask.NoteTaskEntryPoint
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@@ -95,7 +95,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
underTest.onTriggered(expandable = null)
- verify(noteTaskController)
- .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE)
+ verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
new file mode 100644
index 000000000000..52b0b6abda1d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade;
+
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
+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.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.IdRes;
+import android.content.ContentResolver;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.UserManager;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.constraintlayout.widget.ConstraintSet;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardClockSwitch;
+import com.android.keyguard.KeyguardClockSwitchController;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
+import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.view.LongPressHandlingView;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
+import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.ConversationNotificationManager;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+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;
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.TapAgainViewController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.SystemClock;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.List;
+import java.util.Optional;
+
+import dagger.Lazy;
+import kotlinx.coroutines.CoroutineDispatcher;
+
+public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
+
+ protected static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
+ protected static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
+ protected static final int PANEL_WIDTH = 500; // Random value just for the test.
+
+ @Mock protected CentralSurfaces mCentralSurfaces;
+ @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout;
+ @Mock protected KeyguardBottomAreaView mKeyguardBottomArea;
+ @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
+ @Mock protected KeyguardBottomAreaView mQsFrame;
+ @Mock protected HeadsUpManagerPhone mHeadsUpManager;
+ @Mock protected NotificationShelfController mNotificationShelfController;
+ @Mock protected NotificationGutsManager mGutsManager;
+ @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
+ @Mock protected KeyguardUserSwitcherView mUserSwitcherView;
+ @Mock protected ViewStub mUserSwitcherStubView;
+ @Mock protected HeadsUpTouchHelper.Callback mHeadsUpCallback;
+ @Mock protected KeyguardUpdateMonitor mUpdateMonitor;
+ @Mock protected KeyguardBypassController mKeyguardBypassController;
+ @Mock protected DozeParameters mDozeParameters;
+ @Mock protected ScreenOffAnimationController mScreenOffAnimationController;
+ @Mock protected NotificationPanelView mView;
+ @Mock protected LayoutInflater mLayoutInflater;
+ @Mock protected FeatureFlags mFeatureFlags;
+ @Mock protected DynamicPrivacyController mDynamicPrivacyController;
+ @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock protected KeyguardStateController mKeyguardStateController;
+ @Mock protected DozeLog mDozeLog;
+ @Mock protected ShadeLogger mShadeLog;
+ @Mock protected ShadeHeightLogger mShadeHeightLogger;
+ @Mock protected CommandQueue mCommandQueue;
+ @Mock protected VibratorHelper mVibratorHelper;
+ @Mock protected LatencyTracker mLatencyTracker;
+ @Mock protected PowerManager mPowerManager;
+ @Mock protected AccessibilityManager mAccessibilityManager;
+ @Mock protected MetricsLogger mMetricsLogger;
+ @Mock protected Resources mResources;
+ @Mock protected Configuration mConfiguration;
+ @Mock protected KeyguardClockSwitch mKeyguardClockSwitch;
+ @Mock protected MediaHierarchyManager mMediaHierarchyManager;
+ @Mock protected ConversationNotificationManager mConversationNotificationManager;
+ @Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock protected KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+ @Mock protected KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
+ @Mock protected KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent;
+ @Mock protected KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
+ @Mock protected KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
+ @Mock protected KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent;
+ @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
+ @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
+ @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
+ @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
+ @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
+ @Mock protected KeyguardStatusViewController mKeyguardStatusViewController;
+ @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
+ @Mock protected NotificationStackScrollLayoutController
+ mNotificationStackScrollLayoutController;
+ @Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
+ @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock protected AuthController mAuthController;
+ @Mock protected ScrimController mScrimController;
+ @Mock protected MediaDataManager mMediaDataManager;
+ @Mock protected AmbientState mAmbientState;
+ @Mock protected UserManager mUserManager;
+ @Mock protected UiEventLogger mUiEventLogger;
+ @Mock protected LockIconViewController mLockIconViewController;
+ @Mock protected KeyguardMediaController mKeyguardMediaController;
+ @Mock protected NavigationModeController mNavigationModeController;
+ @Mock protected NavigationBarController mNavigationBarController;
+ @Mock protected QuickSettingsController mQsController;
+ @Mock protected ShadeHeaderController mShadeHeaderController;
+ @Mock protected ContentResolver mContentResolver;
+ @Mock protected TapAgainViewController mTapAgainViewController;
+ @Mock protected KeyguardIndicationController mKeyguardIndicationController;
+ @Mock protected FragmentService mFragmentService;
+ @Mock protected FragmentHostManager mFragmentHostManager;
+ @Mock protected NotificationRemoteInputManager mNotificationRemoteInputManager;
+ @Mock protected RecordingController mRecordingController;
+ @Mock protected LockscreenGestureLogger mLockscreenGestureLogger;
+ @Mock protected DumpManager mDumpManager;
+ @Mock protected InteractionJankMonitor mInteractionJankMonitor;
+ @Mock protected NotificationsQSContainerController mNotificationsQSContainerController;
+ @Mock protected QsFrameTranslateController mQsFrameTranslateController;
+ @Mock protected StatusBarWindowStateController mStatusBarWindowStateController;
+ @Mock protected KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock protected NotificationShadeWindowController mNotificationShadeWindowController;
+ @Mock protected SysUiState mSysUiState;
+ @Mock protected NotificationListContainer mNotificationListContainer;
+ @Mock protected NotificationStackSizeCalculator mNotificationStackSizeCalculator;
+ @Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock protected ShadeTransitionController mShadeTransitionController;
+ @Mock protected QS mQs;
+ @Mock protected QSFragment mQSFragment;
+ @Mock protected ViewGroup mQsHeader;
+ @Mock protected ViewParent mViewParent;
+ @Mock protected ViewTreeObserver mViewTreeObserver;
+ @Mock protected KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
+ @Mock protected DreamingToLockscreenTransitionViewModel
+ mDreamingToLockscreenTransitionViewModel;
+ @Mock protected OccludedToLockscreenTransitionViewModel
+ mOccludedToLockscreenTransitionViewModel;
+ @Mock protected LockscreenToDreamingTransitionViewModel
+ mLockscreenToDreamingTransitionViewModel;
+ @Mock protected LockscreenToOccludedTransitionViewModel
+ mLockscreenToOccludedTransitionViewModel;
+ @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
+
+ @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
+ @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @Mock protected MotionEvent mDownMotionEvent;
+ @Mock protected CoroutineDispatcher mMainDispatcher;
+ @Captor
+ protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
+ mEmptySpaceClickListenerCaptor;
+
+ protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
+ protected KeyguardInteractor mKeyguardInteractor;
+ protected NotificationPanelViewController.TouchHandler mTouchHandler;
+ protected ConfigurationController mConfigurationController;
+ protected SysuiStatusBarStateController mStatusBarStateController;
+ protected NotificationPanelViewController mNotificationPanelViewController;
+ protected View.AccessibilityDelegate mAccessibilityDelegate;
+ protected NotificationsQuickSettingsContainer mNotificationContainerParent;
+ protected List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
+ protected Handler mMainHandler;
+ protected View.OnLayoutChangeListener mLayoutChangeListener;
+
+ protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+ protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
+ protected final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ protected final ShadeExpansionStateManager mShadeExpansionStateManager =
+ new ShadeExpansionStateManager();
+
+ protected QuickSettingsController mQuickSettingsController;
+ @Mock protected Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
+
+ protected FragmentHostManager.FragmentListener mFragmentListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMainDispatcher = getMainDispatcher();
+ mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(
+ new FakeKeyguardRepository());
+ mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue,
+ mFeatureFlags, new FakeKeyguardBouncerRepository());
+ SystemClock systemClock = new FakeSystemClock();
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
+ mInteractionJankMonitor, mShadeExpansionStateManager);
+
+ KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
+ keyguardStatusView.setId(R.id.keyguard_status_view);
+
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
+ when(mHeadsUpCallback.getContext()).thenReturn(mContext);
+ when(mView.getResources()).thenReturn(mResources);
+ when(mView.getWidth()).thenReturn(PANEL_WIDTH);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
+ mConfiguration.orientation = ORIENTATION_PORTRAIT;
+ when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+ mDisplayMetrics.density = 100;
+ 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.notification_panel_margin_horizontal))
+ .thenReturn(10);
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
+ .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
+ 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);
+ when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
+ mUserSwitcherStubView);
+ when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
+ when(mView.findViewById(R.id.notification_stack_scroller))
+ .thenReturn(mNotificationStackScrollLayout);
+ when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
+ when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
+ .thenReturn(mHeadsUpCallback);
+ when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
+ when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
+ when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+ when(mView.findViewById(R.id.keyguard_status_view))
+ .thenReturn(mock(KeyguardStatusView.class));
+ mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
+ mNotificationContainerParent.addView(keyguardStatusView);
+ mNotificationContainerParent.onFinishInflate();
+ when(mView.findViewById(R.id.notification_container_parent))
+ .thenReturn(mNotificationContainerParent);
+ when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
+ mDisplayMetrics);
+ when(mKeyguardQsUserSwitchComponentFactory.build(any()))
+ .thenReturn(mKeyguardQsUserSwitchComponent);
+ when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController())
+ .thenReturn(mKeyguardQsUserSwitchController);
+ when(mKeyguardUserSwitcherComponentFactory.build(any()))
+ .thenReturn(mKeyguardUserSwitcherComponent);
+ when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
+ .thenReturn(mKeyguardUserSwitcherController);
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
+ when(mQs.getView()).thenReturn(mView);
+ when(mQSFragment.getView()).thenReturn(mView);
+ doAnswer(invocation -> {
+ mFragmentListener = invocation.getArgument(1);
+ return null;
+ }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any());
+ doAnswer((Answer<Void>) invocation -> {
+ mTouchHandler = invocation.getArgument(0);
+ return null;
+ }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
+
+ // Dreaming->Lockscreen
+ when(mKeyguardTransitionInteractor.getDreamingToLockscreenTransition())
+ .thenReturn(emptyFlow());
+ when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+ when(mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt()))
+ .thenReturn(emptyFlow());
+
+ // Occluded->Lockscreen
+ when(mKeyguardTransitionInteractor.getOccludedToLockscreenTransition())
+ .thenReturn(emptyFlow());
+ when(mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+ when(mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt()))
+ .thenReturn(emptyFlow());
+
+ // Lockscreen->Dreaming
+ when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
+ .thenReturn(emptyFlow());
+ when(mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+ when(mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
+ .thenReturn(emptyFlow());
+
+ // Gone->Dreaming
+ when(mKeyguardTransitionInteractor.getGoneToDreamingTransition())
+ .thenReturn(emptyFlow());
+ when(mGoneToDreamingTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+ when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
+ .thenReturn(emptyFlow());
+
+ // Lockscreen->Occluded
+ when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
+ .thenReturn(emptyFlow());
+ when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha())
+ .thenReturn(emptyFlow());
+ when(mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(anyInt()))
+ .thenReturn(emptyFlow());
+
+ NotificationWakeUpCoordinator coordinator =
+ new NotificationWakeUpCoordinator(
+ mDumpManager,
+ mock(HeadsUpManagerPhone.class),
+ new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
+ mInteractionJankMonitor, mShadeExpansionStateManager),
+ mKeyguardBypassController,
+ mDozeParameters,
+ mScreenOffAnimationController,
+ mock(NotificationWakeUpCoordinatorLogger.class));
+ mConfigurationController = new ConfigurationControllerImpl(mContext);
+ PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
+ mContext,
+ coordinator,
+ mKeyguardBypassController, mHeadsUpManager,
+ mock(NotificationRoundnessManager.class),
+ mConfigurationController,
+ mStatusBarStateController,
+ mFalsingManager,
+ mShadeExpansionStateManager,
+ mLockscreenShadeTransitionController,
+ new FalsingCollectorFake(),
+ mDumpManager);
+ when(mKeyguardStatusViewComponentFactory.build(any()))
+ .thenReturn(mKeyguardStatusViewComponent);
+ when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
+ .thenReturn(mKeyguardClockSwitchController);
+ when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
+ .thenReturn(mKeyguardStatusViewController);
+ when(mKeyguardStatusBarViewComponentFactory.build(any(), any()))
+ .thenReturn(mKeyguardStatusBarViewComponent);
+ when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
+ .thenReturn(mKeyguardStatusBarViewController);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
+ .thenReturn(keyguardStatusView);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
+ .thenReturn(mUserSwitcherView);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
+ .thenReturn(mKeyguardBottomArea);
+ when(mNotificationRemoteInputManager.isRemoteInputActive())
+ .thenReturn(false);
+ when(mInteractionJankMonitor.begin(any(), anyInt()))
+ .thenReturn(true);
+ when(mInteractionJankMonitor.end(anyInt()))
+ .thenReturn(true);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
+ doAnswer(invocation -> {
+ mLayoutChangeListener = invocation.getArgument(0);
+ return null;
+ }).when(mView).addOnLayoutChangeListener(any());
+
+ when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+ when(mView.getParent()).thenReturn(mViewParent);
+ when(mQs.getHeader()).thenReturn(mQsHeader);
+ when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
+ when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+
+ mMainHandler = new Handler(Looper.getMainLooper());
+
+ when(mView.requireViewById(R.id.keyguard_long_press))
+ .thenReturn(mock(LongPressHandlingView.class));
+
+ mNotificationPanelViewController = new NotificationPanelViewController(
+ mView,
+ mMainHandler,
+ mLayoutInflater,
+ mFeatureFlags,
+ coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
+ mFalsingManager, new FalsingCollectorFake(),
+ mKeyguardStateController,
+ mStatusBarStateController,
+ mStatusBarWindowStateController,
+ mNotificationShadeWindowController,
+ mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
+ mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
+ mMetricsLogger,
+ mShadeLog,
+ mShadeHeightLogger,
+ mConfigurationController,
+ () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
+ mConversationNotificationManager, mMediaHierarchyManager,
+ mStatusBarKeyguardViewManager,
+ mGutsManager,
+ mNotificationsQSContainerController,
+ mNotificationStackScrollLayoutController,
+ mKeyguardStatusViewComponentFactory,
+ mKeyguardQsUserSwitchComponentFactory,
+ mKeyguardUserSwitcherComponentFactory,
+ mKeyguardStatusBarViewComponentFactory,
+ mLockscreenShadeTransitionController,
+ mAuthController,
+ mScrimController,
+ mUserManager,
+ mMediaDataManager,
+ mNotificationShadeDepthController,
+ mAmbientState,
+ mLockIconViewController,
+ mKeyguardMediaController,
+ mTapAgainViewController,
+ mNavigationModeController,
+ mNavigationBarController,
+ mQsController,
+ mFragmentService,
+ mContentResolver,
+ mRecordingController,
+ mShadeHeaderController,
+ mScreenOffAnimationController,
+ mLockscreenGestureLogger,
+ mShadeExpansionStateManager,
+ mNotificationRemoteInputManager,
+ mSysUIUnfoldComponent,
+ mSysUiState,
+ () -> mKeyguardBottomAreaViewController,
+ mKeyguardUnlockAnimationController,
+ mKeyguardIndicationController,
+ mNotificationListContainer,
+ mNotificationStackSizeCalculator,
+ mUnlockedScreenOffAnimationController,
+ mShadeTransitionController,
+ systemClock,
+ mKeyguardBottomAreaViewModel,
+ mKeyguardBottomAreaInteractor,
+ mAlternateBouncerInteractor,
+ mDreamingToLockscreenTransitionViewModel,
+ mOccludedToLockscreenTransitionViewModel,
+ mLockscreenToDreamingTransitionViewModel,
+ mGoneToDreamingTransitionViewModel,
+ mLockscreenToOccludedTransitionViewModel,
+ mMainDispatcher,
+ mKeyguardTransitionInteractor,
+ mDumpManager,
+ mKeyuardLongPressViewModel,
+ mKeyguardInteractor);
+ mNotificationPanelViewController.initDependencies(
+ mCentralSurfaces,
+ null,
+ () -> {},
+ mNotificationShelfController);
+ mNotificationPanelViewController.setTrackingStartedListener(() -> {});
+ mNotificationPanelViewController.setOpenCloseListener(
+ new NotificationPanelViewController.OpenCloseListener() {
+ @Override
+ public void onClosingFinished() {}
+
+ @Override
+ public void onOpenStarted() {}
+ });
+ mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
+ ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ verify(mView, atLeast(1)).addOnAttachStateChangeListener(
+ onAttachStateChangeListenerArgumentCaptor.capture());
+ mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues();
+
+ ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor =
+ ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
+ verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
+ mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue();
+ mNotificationPanelViewController.getStatusBarStateController()
+ .addCallback(mNotificationPanelViewController.getStatusBarStateListener());
+ mNotificationPanelViewController
+ .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
+ verify(mNotificationStackScrollLayoutController)
+ .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture());
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ reset(mKeyguardStatusViewController);
+
+ when(mNotificationPanelViewControllerLazy.get())
+ .thenReturn(mNotificationPanelViewController);
+ mQuickSettingsController = new QuickSettingsController(
+ mNotificationPanelViewControllerLazy,
+ mView,
+ mQsFrameTranslateController,
+ mShadeTransitionController,
+ expansionHandler,
+ mNotificationRemoteInputManager,
+ mShadeExpansionStateManager,
+ mStatusBarKeyguardViewManager,
+ mNotificationStackScrollLayoutController,
+ mLockscreenShadeTransitionController,
+ mNotificationShadeDepthController,
+ mShadeHeaderController,
+ mStatusBarTouchableRegionManager,
+ mKeyguardStateController,
+ mKeyguardBypassController,
+ mUpdateMonitor,
+ mScrimController,
+ mMediaDataManager,
+ mMediaHierarchyManager,
+ mAmbientState,
+ mRecordingController,
+ mFalsingManager,
+ new FalsingCollectorFake(),
+ mAccessibilityManager,
+ mLockscreenGestureLogger,
+ mMetricsLogger,
+ mFeatureFlags,
+ mInteractionJankMonitor,
+ mShadeLog
+ );
+ }
+
+ @After
+ public void tearDown() {
+ mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
+ mNotificationPanelViewController.cancelHeightAnimator();
+ mMainHandler.removeCallbacksAndMessages(null);
+ }
+
+ protected void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
+ int ambientPadding) {
+
+ when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
+ when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
+ when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
+ when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
+
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
+ .thenReturn(indicationPadding);
+ mNotificationPanelViewController.loadDimens();
+
+ mNotificationPanelViewController.setAmbientIndicationTop(
+ /* ambientIndicationTop= */ stackBottom - ambientPadding,
+ /* ambientTextVisible= */ true);
+ }
+
+ protected void triggerPositionClockAndNotifications() {
+ mNotificationPanelViewController.onQsSetExpansionHeightCalled(false);
+ }
+
+ protected FalsingManager.FalsingTapListener getFalsingTapListener() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewAttachedToWindow(mView);
+ }
+ assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1);
+ return mFalsingManager.getTapListeners().get(0);
+ }
+
+ protected void givenViewAttached() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewAttachedToWindow(mView);
+ }
+ }
+
+ protected ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) {
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ return constraintSet.getConstraint(id).layout;
+ }
+
+ protected void enableSplitShade(boolean enabled) {
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
+ mNotificationPanelViewController.updateResources();
+ }
+
+ protected void updateMultiUserSetting(boolean enabled) {
+ when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
+ when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled);
+ final ArgumentCaptor<ContentObserver> observerCaptor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver)
+ .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
+ observerCaptor.getValue().onChange(/* selfChange */ false);
+ }
+
+ protected void updateSmallestScreenWidth(int smallestScreenWidthDp) {
+ Configuration configuration = new Configuration();
+ configuration.smallestScreenWidthDp = smallestScreenWidthDp;
+ mConfigurationController.onConfigurationChanged(configuration);
+ }
+
+ protected void onTouchEvent(MotionEvent ev) {
+ mTouchHandler.onTouch(mView, ev);
+ }
+
+ protected void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+ mNotificationPanelViewController.setDozing(
+ /* dozing= */ dozing,
+ /* animate= */ false
+ );
+ }
+
+ protected void assertKeyguardStatusViewCentered() {
+ mNotificationPanelViewController.updateResources();
+ assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf(
+ ConstraintSet.PARENT_ID, ConstraintSet.UNSET);
+ }
+
+ protected void assertKeyguardStatusViewNotCentered() {
+ mNotificationPanelViewController.updateResources();
+ assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo(
+ R.id.qs_edge_guideline);
+ }
+
+ protected void setIsFullWidth(boolean fullWidth) {
+ float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f;
+ when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth);
+ triggerLayoutChange();
+ }
+
+ protected void triggerLayoutChange() {
+ mLayoutChangeListener.onLayoutChange(
+ mView,
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ 0,
+ /* bottom= */ 0,
+ /* oldLeft= */ 0,
+ /* oldTop= */ 0,
+ /* oldRight= */ 0,
+ /* oldBottom= */ 0
+ );
+ }
+
+ protected CoroutineDispatcher getMainDispatcher() {
+ return mMainDispatcher;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 8f6653f1d601..d86ff671a222 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.shade;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
@@ -31,592 +29,72 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
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;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.IdRes;
-import android.content.ContentResolver;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.FaceAuthApiRequestReason;
-import com.android.keyguard.KeyguardClockSwitch;
-import com.android.keyguard.KeyguardClockSwitchController;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.keyguard.KeyguardStatusViewController;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LockIconViewController;
-import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
-import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.common.ui.view.LongPressHandlingView;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.shade.transition.ShadeTransitionController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.QsFrameTranslateController;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.ConversationNotificationManager;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
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;
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.TapAgainViewController;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
-import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.util.time.SystemClock;
-import com.android.wm.shell.animation.FlingAnimationUtils;
-
-import org.junit.After;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
import java.util.List;
-import java.util.Optional;
-
-import dagger.Lazy;
-import kotlinx.coroutines.CoroutineDispatcher;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NotificationPanelViewControllerTest extends SysuiTestCase {
-
- private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
- private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
- private static final int PANEL_WIDTH = 500; // Random value just for the test.
-
- @Mock private CentralSurfaces mCentralSurfaces;
- @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
- @Mock private KeyguardBottomAreaView mKeyguardBottomArea;
- @Mock private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
- @Mock private KeyguardBottomAreaView mQsFrame;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
- @Mock private NotificationShelfController mNotificationShelfController;
- @Mock private NotificationGutsManager mGutsManager;
- @Mock private KeyguardStatusBarView mKeyguardStatusBar;
- @Mock private KeyguardUserSwitcherView mUserSwitcherView;
- @Mock private ViewStub mUserSwitcherStubView;
- @Mock private HeadsUpTouchHelper.Callback mHeadsUpCallback;
- @Mock private KeyguardUpdateMonitor mUpdateMonitor;
- @Mock private KeyguardBypassController mKeyguardBypassController;
- @Mock private DozeParameters mDozeParameters;
- @Mock private ScreenOffAnimationController mScreenOffAnimationController;
- @Mock private NotificationPanelView mView;
- @Mock private LayoutInflater mLayoutInflater;
- @Mock private FeatureFlags mFeatureFlags;
- @Mock private DynamicPrivacyController mDynamicPrivacyController;
- @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- @Mock private KeyguardStateController mKeyguardStateController;
- @Mock private DozeLog mDozeLog;
- @Mock private ShadeLogger mShadeLog;
- @Mock private ShadeHeightLogger mShadeHeightLogger;
- @Mock private CommandQueue mCommandQueue;
- @Mock private VibratorHelper mVibratorHelper;
- @Mock private LatencyTracker mLatencyTracker;
- @Mock private PowerManager mPowerManager;
- @Mock private AccessibilityManager mAccessibilityManager;
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private Resources mResources;
- @Mock private Configuration mConfiguration;
- @Mock private KeyguardClockSwitch mKeyguardClockSwitch;
- @Mock private MediaHierarchyManager mMediaHierarchyManager;
- @Mock private ConversationNotificationManager mConversationNotificationManager;
- @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
- @Mock private KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent;
- @Mock private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
- @Mock private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
- @Mock private KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent;
- @Mock private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
- @Mock private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
- @Mock private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
- @Mock private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
- @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController;
- @Mock private KeyguardStatusViewController mKeyguardStatusViewController;
- @Mock private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
- @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
- @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private AuthController mAuthController;
- @Mock private ScrimController mScrimController;
- @Mock private MediaDataManager mMediaDataManager;
- @Mock private AmbientState mAmbientState;
- @Mock private UserManager mUserManager;
- @Mock private UiEventLogger mUiEventLogger;
- @Mock private LockIconViewController mLockIconViewController;
- @Mock private KeyguardMediaController mKeyguardMediaController;
- @Mock private NavigationModeController mNavigationModeController;
- @Mock private NavigationBarController mNavigationBarController;
- @Mock private QuickSettingsController mQsController;
- @Mock private ShadeHeaderController mShadeHeaderController;
- @Mock private ContentResolver mContentResolver;
- @Mock private TapAgainViewController mTapAgainViewController;
- @Mock private KeyguardIndicationController mKeyguardIndicationController;
- @Mock private FragmentService mFragmentService;
- @Mock private FragmentHostManager mFragmentHostManager;
- @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
- @Mock private RecordingController mRecordingController;
- @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
- @Mock private DumpManager mDumpManager;
- @Mock private InteractionJankMonitor mInteractionJankMonitor;
- @Mock private NotificationsQSContainerController mNotificationsQSContainerController;
- @Mock private QsFrameTranslateController mQsFrameTranslateController;
- @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
- @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
- @Mock private SysUiState mSysUiState;
- @Mock private NotificationListContainer mNotificationListContainer;
- @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- @Mock private ShadeTransitionController mShadeTransitionController;
- @Mock private QS mQs;
- @Mock private QSFragment mQSFragment;
- @Mock private ViewGroup mQsHeader;
- @Mock private ViewParent mViewParent;
- @Mock private ViewTreeObserver mViewTreeObserver;
- @Mock private KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
- @Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
- @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
- @Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
- @Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
- @Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
- @Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
- @Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-
- @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
- @Mock private KeyguardInteractor mKeyguardInteractor;
- @Mock private KeyguardLongPressViewModel mKeyuardLongPressViewModel;
- @Mock private CoroutineDispatcher mMainDispatcher;
- @Mock private MotionEvent mDownMotionEvent;
- @Captor
- private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
- mEmptySpaceClickListenerCaptor;
-
- private NotificationPanelViewController.TouchHandler mTouchHandler;
- private ConfigurationController mConfigurationController;
- private SysuiStatusBarStateController mStatusBarStateController;
- private NotificationPanelViewController mNotificationPanelViewController;
- private View.AccessibilityDelegate mAccessibilityDelegate;
- private NotificationsQuickSettingsContainer mNotificationContainerParent;
- private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
- private Handler mMainHandler;
- private View.OnLayoutChangeListener mLayoutChangeListener;
-
- private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
- private final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
- private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
- private final ShadeExpansionStateManager mShadeExpansionStateManager =
- new ShadeExpansionStateManager();
-
- private QuickSettingsController mQuickSettingsController;
- @Mock private Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
-
- private FragmentHostManager.FragmentListener mFragmentListener;
+public class NotificationPanelViewControllerTest extends NotificationPanelViewControllerBaseTest {
@Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- SystemClock systemClock = new FakeSystemClock();
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
-
- KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
- keyguardStatusView.setId(R.id.keyguard_status_view);
+ public void before() {
DejankUtils.setImmediate(true);
+ }
- when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
- when(mHeadsUpCallback.getContext()).thenReturn(mContext);
- when(mView.getResources()).thenReturn(mResources);
- when(mView.getWidth()).thenReturn(PANEL_WIDTH);
- when(mResources.getConfiguration()).thenReturn(mConfiguration);
- mConfiguration.orientation = ORIENTATION_PORTRAIT;
- when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
- mDisplayMetrics.density = 100;
- 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.notification_panel_margin_horizontal))
- .thenReturn(10);
- when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
- .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
- 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);
- when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
- mUserSwitcherStubView);
- when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
- when(mView.findViewById(R.id.notification_stack_scroller))
- .thenReturn(mNotificationStackScrollLayout);
- when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
- when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
- .thenReturn(mHeadsUpCallback);
- when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
- when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
- when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
- when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
- when(mView.findViewById(R.id.keyguard_status_view))
- .thenReturn(mock(KeyguardStatusView.class));
- mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
- mNotificationContainerParent.addView(keyguardStatusView);
- mNotificationContainerParent.onFinishInflate();
- when(mView.findViewById(R.id.notification_container_parent))
- .thenReturn(mNotificationContainerParent);
- when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
- mDisplayMetrics);
- when(mKeyguardQsUserSwitchComponentFactory.build(any()))
- .thenReturn(mKeyguardQsUserSwitchComponent);
- when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController())
- .thenReturn(mKeyguardQsUserSwitchController);
- when(mKeyguardUserSwitcherComponentFactory.build(any()))
- .thenReturn(mKeyguardUserSwitcherComponent);
- when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
- .thenReturn(mKeyguardUserSwitcherController);
- when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
- when(mQs.getView()).thenReturn(mView);
- when(mQSFragment.getView()).thenReturn(mView);
- doAnswer(invocation -> {
- mFragmentListener = invocation.getArgument(1);
- return null;
- }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any());
- doAnswer((Answer<Void>) invocation -> {
- mTouchHandler = invocation.getArgument(0);
- return null;
- }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
-
- NotificationWakeUpCoordinator coordinator =
- new NotificationWakeUpCoordinator(
- mDumpManager,
- mock(HeadsUpManagerPhone.class),
- new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager),
- mKeyguardBypassController,
- mDozeParameters,
- mScreenOffAnimationController,
- mock(NotificationWakeUpCoordinatorLogger.class));
- mConfigurationController = new ConfigurationControllerImpl(mContext);
- PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
- mContext,
- coordinator,
- mKeyguardBypassController, mHeadsUpManager,
- mock(NotificationRoundnessManager.class),
- mConfigurationController,
- mStatusBarStateController,
- mFalsingManager,
- mShadeExpansionStateManager,
- mLockscreenShadeTransitionController,
- new FalsingCollectorFake(),
- mDumpManager);
- when(mKeyguardStatusViewComponentFactory.build(any()))
- .thenReturn(mKeyguardStatusViewComponent);
- when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
- .thenReturn(mKeyguardClockSwitchController);
- when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
- .thenReturn(mKeyguardStatusViewController);
- when(mKeyguardStatusBarViewComponentFactory.build(any(), any()))
- .thenReturn(mKeyguardStatusBarViewComponent);
- when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
- .thenReturn(mKeyguardStatusBarViewController);
- when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
- .thenReturn(keyguardStatusView);
- when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
- .thenReturn(mUserSwitcherView);
- when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
- .thenReturn(mKeyguardBottomArea);
- when(mNotificationRemoteInputManager.isRemoteInputActive())
- .thenReturn(false);
- when(mInteractionJankMonitor.begin(any(), anyInt()))
- .thenReturn(true);
- when(mInteractionJankMonitor.end(anyInt()))
- .thenReturn(true);
- doAnswer(invocation -> {
- ((Runnable) invocation.getArgument(0)).run();
- return null;
- }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
- doAnswer(invocation -> {
- mLayoutChangeListener = invocation.getArgument(0);
- return null;
- }).when(mView).addOnLayoutChangeListener(any());
-
- when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
- when(mView.getParent()).thenReturn(mViewParent);
- when(mQs.getHeader()).thenReturn(mQsHeader);
- when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
-
- mMainHandler = new Handler(Looper.getMainLooper());
-
- when(mView.requireViewById(R.id.keyguard_long_press))
- .thenReturn(mock(LongPressHandlingView.class));
-
- mNotificationPanelViewController = new NotificationPanelViewController(
- mView,
- mMainHandler,
- mLayoutInflater,
- mFeatureFlags,
- coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
- mFalsingManager, new FalsingCollectorFake(),
- mKeyguardStateController,
- mStatusBarStateController,
- mStatusBarWindowStateController,
- mNotificationShadeWindowController,
- mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
- mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
- mMetricsLogger,
- mShadeLog,
- mShadeHeightLogger,
- mConfigurationController,
- () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
- mConversationNotificationManager, mMediaHierarchyManager,
- mStatusBarKeyguardViewManager,
- mGutsManager,
- mNotificationsQSContainerController,
- mNotificationStackScrollLayoutController,
- mKeyguardStatusViewComponentFactory,
- mKeyguardQsUserSwitchComponentFactory,
- mKeyguardUserSwitcherComponentFactory,
- mKeyguardStatusBarViewComponentFactory,
- mLockscreenShadeTransitionController,
- mAuthController,
- mScrimController,
- mUserManager,
- mMediaDataManager,
- mNotificationShadeDepthController,
- mAmbientState,
- mLockIconViewController,
- mKeyguardMediaController,
- mTapAgainViewController,
- mNavigationModeController,
- mNavigationBarController,
- mQsController,
- mFragmentService,
- mContentResolver,
- mRecordingController,
- mShadeHeaderController,
- mScreenOffAnimationController,
- mLockscreenGestureLogger,
- mShadeExpansionStateManager,
- mNotificationRemoteInputManager,
- mSysUIUnfoldComponent,
- mSysUiState,
- () -> mKeyguardBottomAreaViewController,
- mKeyguardUnlockAnimationController,
- mKeyguardIndicationController,
- mNotificationListContainer,
- mNotificationStackSizeCalculator,
- mUnlockedScreenOffAnimationController,
- mShadeTransitionController,
- systemClock,
- mKeyguardBottomAreaViewModel,
- mKeyguardBottomAreaInteractor,
- mAlternateBouncerInteractor,
- mDreamingToLockscreenTransitionViewModel,
- mOccludedToLockscreenTransitionViewModel,
- mLockscreenToDreamingTransitionViewModel,
- mGoneToDreamingTransitionViewModel,
- mLockscreenToOccludedTransitionViewModel,
- mMainDispatcher,
- mKeyguardTransitionInteractor,
- mDumpManager,
- mKeyuardLongPressViewModel,
- mKeyguardInteractor);
- mNotificationPanelViewController.initDependencies(
- mCentralSurfaces,
- null,
- () -> {},
- mNotificationShelfController);
- mNotificationPanelViewController.setTrackingStartedListener(() -> {});
- mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
- @Override
- public void onClosingFinished() {}
-
- @Override
- public void onOpenStarted() {}
- });
- mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
- verify(mView, atLeast(1)).addOnAttachStateChangeListener(
- onAttachStateChangeListenerArgumentCaptor.capture());
- mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues();
-
- ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor =
- ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
- verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
- mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue();
- mNotificationPanelViewController.getStatusBarStateController()
- .addCallback(mNotificationPanelViewController.getStatusBarStateListener());
- mNotificationPanelViewController
- .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
- verify(mNotificationStackScrollLayoutController)
- .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture());
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
- reset(mKeyguardStatusViewController);
-
- when(mNotificationPanelViewControllerLazy.get())
- .thenReturn(mNotificationPanelViewController);
- mQuickSettingsController = new QuickSettingsController(
- mNotificationPanelViewControllerLazy,
- mView,
- mQsFrameTranslateController,
- mShadeTransitionController,
- expansionHandler,
- mNotificationRemoteInputManager,
- mShadeExpansionStateManager,
- mStatusBarKeyguardViewManager,
- mNotificationStackScrollLayoutController,
- mLockscreenShadeTransitionController,
- mNotificationShadeDepthController,
- mShadeHeaderController,
- mStatusBarTouchableRegionManager,
- mKeyguardStateController,
- mKeyguardBypassController,
- mUpdateMonitor,
- mScrimController,
- mMediaDataManager,
- mMediaHierarchyManager,
- mAmbientState,
- mRecordingController,
- mFalsingManager,
- new FalsingCollectorFake(),
- mAccessibilityManager,
- mLockscreenGestureLogger,
- mMetricsLogger,
- mFeatureFlags,
- mInteractionJankMonitor,
- mShadeLog
- );
+ /**
+ * When the Back gesture starts (progress 0%), the scrim will stay at 100% scale (1.0f).
+ */
+ @Test
+ public void testBackGesture_min_scrimAtMaxScale() {
+ mNotificationPanelViewController.onBackProgressed(0.0f);
+ verify(mScrimController).applyBackScaling(1.0f);
}
- @After
- public void tearDown() {
- mNotificationPanelViewController.cancelHeightAnimator();
- mMainHandler.removeCallbacksAndMessages(null);
+ /**
+ * When the Back gesture is at max (progress 100%), the scrim will be scaled to its minimum.
+ */
+ @Test
+ public void testBackGesture_max_scrimAtMinScale() {
+ mNotificationPanelViewController.onBackProgressed(1.0f);
+ verify(mScrimController).applyBackScaling(
+ NotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE);
}
@Test
@@ -671,23 +149,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.isNotEqualTo(-1);
}
- private void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
- int ambientPadding) {
-
- when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
- when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
- when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
- when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
-
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
- .thenReturn(indicationPadding);
- mNotificationPanelViewController.loadDimens();
-
- mNotificationPanelViewController.setAmbientIndicationTop(
- /* ambientIndicationTop= */ stackBottom - ambientPadding,
- /* ambientTextVisible= */ true);
- }
-
@Test
@Ignore("b/261472011 - Test appears inconsistent across environments")
public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() {
@@ -992,68 +453,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
- givenViewAttached();
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
- updateMultiUserSetting(true);
- clearInvocations(mView);
-
- updateMultiUserSetting(false);
-
- ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
- verify(mView, atLeastOnce()).addView(captor.capture(), anyInt());
- final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(),
- view -> view.getId() == R.id.keyguard_user_switcher_stub);
- assertThat(userSwitcherStub).isNotNull();
- assertThat(userSwitcherStub).isInstanceOf(ViewStub.class);
- }
-
- @Test
- public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() {
- givenViewAttached();
- when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null);
- updateSmallestScreenWidth(300);
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
- when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
- when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true);
-
- updateSmallestScreenWidth(800);
-
- verify(mUserSwitcherStubView).inflate();
- }
-
- @Test
- public void testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() {
- givenViewAttached();
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
- when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
- when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
- .thenReturn(false);
-
- mNotificationPanelViewController.onFinishInflate();
-
- verify(mUserSwitcherStubView, never()).inflate();
- verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true);
- }
-
- @Test
- public void testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() {
- givenViewAttached();
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
- when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
- when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
- .thenReturn(false);
-
- mNotificationPanelViewController.reInflateViews();
-
- verify(mUserSwitcherStubView, never()).inflate();
- }
-
- @Test
public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
mStatusBarStateController.setState(KEYGUARD);
@@ -1129,26 +528,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void testDoubleTapRequired_Keyguard() {
- FalsingManager.FalsingTapListener listener = getFalsingTapListener();
- mStatusBarStateController.setState(KEYGUARD);
-
- listener.onAdditionalTapRequired();
-
- verify(mKeyguardIndicationController).showTransientIndication(anyInt());
- }
-
- @Test
- public void testDoubleTapRequired_ShadeLocked() {
- FalsingManager.FalsingTapListener listener = getFalsingTapListener();
- mStatusBarStateController.setState(SHADE_LOCKED);
-
- listener.onAdditionalTapRequired();
-
- verify(mTapAgainViewController).show();
- }
-
- @Test
public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() {
mStatusBarStateController.setState(KEYGUARD);
when(mQsController.getExpanded()).thenReturn(true);
@@ -1423,19 +802,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void testOnAttachRefreshStatusBarState() {
- mStatusBarStateController.setState(KEYGUARD);
- when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false);
- for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
- listener.onViewAttachedToWindow(mView);
- }
- verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility(
- KEYGUARD/*statusBarState*/,
- false/*keyguardFadingAway*/,
- false/*goingToFullShade*/, SHADE/*oldStatusBarState*/);
- }
-
- @Test
public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
enableSplitShade(true);
mNotificationPanelViewController.expandWithQs();
@@ -1635,98 +1001,4 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mStatusBarStateController.setState(SHADE_LOCKED);
assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue();
}
-
- private static MotionEvent createMotionEvent(int x, int y, int action) {
- return MotionEvent.obtain(
- /* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
- }
-
- private void triggerPositionClockAndNotifications() {
- mNotificationPanelViewController.onQsSetExpansionHeightCalled(false);
- }
-
- private FalsingManager.FalsingTapListener getFalsingTapListener() {
- for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
- listener.onViewAttachedToWindow(mView);
- }
- assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1);
- return mFalsingManager.getTapListeners().get(0);
- }
-
- private void givenViewAttached() {
- for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
- listener.onViewAttachedToWindow(mView);
- }
- }
-
- private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) {
- ConstraintSet constraintSet = new ConstraintSet();
- constraintSet.clone(mNotificationContainerParent);
- return constraintSet.getConstraint(id).layout;
- }
-
- private void enableSplitShade(boolean enabled) {
- when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
- mNotificationPanelViewController.updateResources();
- }
-
- private void updateMultiUserSetting(boolean enabled) {
- when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false);
- when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled);
- final ArgumentCaptor<ContentObserver> observerCaptor =
- ArgumentCaptor.forClass(ContentObserver.class);
- verify(mContentResolver)
- .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
- observerCaptor.getValue().onChange(/* selfChange */ false);
- }
-
- private void updateSmallestScreenWidth(int smallestScreenWidthDp) {
- Configuration configuration = new Configuration();
- configuration.smallestScreenWidthDp = smallestScreenWidthDp;
- mConfigurationController.onConfigurationChanged(configuration);
- }
-
- private void onTouchEvent(MotionEvent ev) {
- mTouchHandler.onTouch(mView, ev);
- }
-
- private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
- when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
- mNotificationPanelViewController.setDozing(
- /* dozing= */ dozing,
- /* animate= */ false
- );
- }
-
- private void assertKeyguardStatusViewCentered() {
- mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf(
- ConstraintSet.PARENT_ID, ConstraintSet.UNSET);
- }
-
- private void assertKeyguardStatusViewNotCentered() {
- mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo(
- R.id.qs_edge_guideline);
- }
-
- private void setIsFullWidth(boolean fullWidth) {
- float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f;
- when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth);
- triggerLayoutChange();
- }
-
- private void triggerLayoutChange() {
- mLayoutChangeListener.onLayoutChange(
- mView,
- /* left= */ 0,
- /* top= */ 0,
- /* right= */ 0,
- /* bottom= */ 0,
- /* oldLeft= */ 0,
- /* oldTop= */ 0,
- /* oldRight= */ 0,
- /* oldBottom= */ 0
- );
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
new file mode 100644
index 000000000000..0c046e93ee20
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.view.ViewStub
+import androidx.test.filters.SmallTest
+import com.android.internal.util.CollectionUtils
+import com.android.keyguard.KeyguardClockSwitch.LARGE
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NotificationPanelViewControllerWithCoroutinesTest :
+ NotificationPanelViewControllerBaseTest() {
+
+ @Captor private lateinit var viewCaptor: ArgumentCaptor<View>
+
+ override fun getMainDispatcher() = Dispatchers.Main.immediate
+
+ @Test
+ fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest {
+ launch(Dispatchers.Main.immediate) { givenViewAttached() }
+ advanceUntilIdle()
+
+ whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
+ .thenReturn(true)
+ updateMultiUserSetting(true)
+ clearInvocations(mView)
+
+ updateMultiUserSetting(false)
+
+ verify(mView, atLeastOnce()).addView(viewCaptor.capture(), anyInt())
+ val userSwitcherStub =
+ CollectionUtils.find(
+ viewCaptor.getAllValues(),
+ { view -> view.getId() == R.id.keyguard_user_switcher_stub }
+ )
+ assertThat(userSwitcherStub).isNotNull()
+ assertThat(userSwitcherStub).isInstanceOf(ViewStub::class.java)
+ }
+
+ @Test
+ fun testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() = runTest {
+ launch(Dispatchers.Main.immediate) { givenViewAttached() }
+ advanceUntilIdle()
+
+ whenever(mView.findViewById<View>(R.id.keyguard_user_switcher_view)).thenReturn(null)
+ updateSmallestScreenWidth(300)
+ whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
+ .thenReturn(true)
+ whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
+ .thenReturn(false)
+ whenever(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true)
+
+ updateSmallestScreenWidth(800)
+
+ verify(mUserSwitcherStubView).inflate()
+ }
+
+ @Test
+ fun testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() = runTest {
+ launch(Dispatchers.Main.immediate) { givenViewAttached() }
+ advanceUntilIdle()
+
+ whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
+ .thenReturn(true)
+ whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
+ .thenReturn(false)
+ whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
+ .thenReturn(false)
+
+ mNotificationPanelViewController.onFinishInflate()
+
+ verify(mUserSwitcherStubView, never()).inflate()
+ verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true)
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() = runTest {
+ launch(Dispatchers.Main.immediate) { givenViewAttached() }
+ advanceUntilIdle()
+
+ whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher))
+ .thenReturn(true)
+ whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))
+ .thenReturn(false)
+ whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */))
+ .thenReturn(false)
+
+ mNotificationPanelViewController.reInflateViews()
+
+ verify(mUserSwitcherStubView, never()).inflate()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun testDoubleTapRequired_Keyguard() = runTest {
+ launch(Dispatchers.Main.immediate) {
+ val listener = getFalsingTapListener()
+ mStatusBarStateController.setState(KEYGUARD)
+
+ listener.onAdditionalTapRequired()
+
+ verify(mKeyguardIndicationController).showTransientIndication(anyInt())
+ }
+ advanceUntilIdle()
+ }
+
+ @Test
+ fun testDoubleTapRequired_ShadeLocked() = runTest {
+ launch(Dispatchers.Main.immediate) {
+ val listener = getFalsingTapListener()
+ mStatusBarStateController.setState(SHADE_LOCKED)
+
+ listener.onAdditionalTapRequired()
+
+ verify(mTapAgainViewController).show()
+ }
+ advanceUntilIdle()
+ }
+
+ @Test
+ fun testOnAttachRefreshStatusBarState() = runTest {
+ launch(Dispatchers.Main.immediate) {
+ mStatusBarStateController.setState(KEYGUARD)
+ whenever(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false)
+ mOnAttachStateChangeListeners.forEach { it.onViewAttachedToWindow(mView) }
+ verify(mKeyguardStatusViewController)
+ .setKeyguardStatusViewVisibility(
+ KEYGUARD /*statusBarState*/,
+ false /*keyguardFadingAway*/,
+ false /*goingToFullShade*/,
+ SHADE /*oldStatusBarState*/
+ )
+ }
+ advanceUntilIdle()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index d229a08ad7c4..0a401b09b6cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -28,10 +28,10 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.emptyFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,8 +82,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock
- private lateinit var featureFlags: FeatureFlags
- @Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@@ -124,6 +123,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
+ whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
+ .thenReturn(emptyFlow<TransitionStep>())
underTest = NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
FalsingCollectorFake(),
@@ -143,7 +144,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
notificationInsetsController,
ambientState,
pulsingGestureListener,
- featureFlags,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
alternateBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 5e9c2199897d..5d719790386a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -40,7 +42,6 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -93,7 +94,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private AmbientState mAmbientState;
@Mock private PulsingGestureListener mPulsingGestureListener;
- @Mock private FeatureFlags mFeatureFlags;
@Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
@Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
@Mock private KeyguardBouncerComponent mKeyguardBouncerComponent;
@@ -125,6 +125,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
when(mDockManager.isDocked()).thenReturn(false);
+ when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
+ .thenReturn(emptyFlow());
+
mController = new NotificationShadeWindowViewController(
mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
@@ -144,7 +147,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mNotificationInsetsController,
mAmbientState,
mPulsingGestureListener,
- mFeatureFlags,
mKeyguardBouncerViewModel,
mKeyguardBouncerComponentFactory,
mAlternateBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
index bea2cfb8bb5c..bedb2b33d07b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
@@ -73,7 +73,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
- when(Utilities.isTablet(mContext)).thenReturn(true);
+ when(Utilities.isLargeScreen(mContext)).thenReturn(true);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
@@ -90,7 +90,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
- when(Utilities.isTablet(mContext)).thenReturn(false);
+ when(Utilities.isLargeScreen(mContext)).thenReturn(false);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
@@ -107,7 +107,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
- when(Utilities.isTablet(mContext)).thenReturn(true);
+ when(Utilities.isLargeScreen(mContext)).thenReturn(true);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
@@ -124,7 +124,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
- when(Utilities.isTablet(mContext)).thenReturn(false);
+ when(Utilities.isLargeScreen(mContext)).thenReturn(false);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt
deleted file mode 100644
index a6a9e51aa555..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.fsi
-
-import android.R
-import android.app.Notification
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.UserHandle
-import android.service.dreams.IDreamManager
-import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
-import com.android.systemui.statusbar.phone.CentralSurfaces
-import java.util.concurrent.Executor
-import junit.framework.Assert.assertEquals
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
-class FsiChromeRepoTest : SysuiTestCase() {
-
- @Mock lateinit var centralSurfaces: CentralSurfaces
- @Mock lateinit var fsiChromeRepo: FsiChromeRepo
- @Mock lateinit var packageManager: PackageManager
-
- var keyguardRepo = FakeKeyguardRepository()
- @Mock private lateinit var applicationInfo: ApplicationInfo
-
- @Mock lateinit var launchFullScreenIntentProvider: LaunchFullScreenIntentProvider
- var featureFlags = FakeFeatureFlags()
- @Mock lateinit var dreamManager: IDreamManager
-
- // Execute all foreground & background requests immediately
- private val uiBgExecutor = Executor { r -> r.run() }
-
- private val appName: String = "appName"
- private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android)
- private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- // Set up package manager mocks
- whenever(packageManager.getApplicationIcon(anyString())).thenReturn(appIcon)
- whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
- .thenReturn(appIcon)
- whenever(packageManager.getApplicationLabel(any())).thenReturn(appName)
- mContext.setMockPackageManager(packageManager)
-
- fsiChromeRepo =
- FsiChromeRepo(
- mContext,
- packageManager,
- keyguardRepo,
- launchFullScreenIntentProvider,
- featureFlags,
- uiBgExecutor,
- dreamManager,
- centralSurfaces
- )
- }
-
- private fun createFsiEntry(fsi: PendingIntent): NotificationEntry {
- val nb =
- Notification.Builder(mContext, "a")
- .setContentTitle("foo")
- .setSmallIcon(R.drawable.sym_def_app_icon)
- .setFullScreenIntent(fsi, /* highPriority= */ true)
-
- val sbn =
- StatusBarNotification(
- "pkg",
- "opPkg",
- /* id= */ 0,
- "tag" + System.currentTimeMillis(),
- /* uid= */ 0,
- /* initialPid */ 0,
- nb.build(),
- UserHandle(0),
- /* overrideGroupKey= */ null,
- /* postTime= */ 0
- )
-
- val entry = Mockito.mock(NotificationEntry::class.java)
- whenever(entry.importance).thenReturn(NotificationManager.IMPORTANCE_HIGH)
- whenever(entry.sbn).thenReturn(sbn)
- return entry
- }
-
- @Test
- fun testLaunchFullscreenIntent_flagNotEnabled_noLaunch() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, false)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent()
- }
-
- @Test
- fun testLaunchFullscreenIntent_notOnKeyguard_noLaunch() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, true)
- keyguardRepo.setKeyguardShowing(false)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent()
- }
-
- @Test
- fun testLaunchFullscreenIntent_stopsScreensaver() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, true)
- keyguardRepo.setKeyguardShowing(true)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- Mockito.verify(dreamManager, times(1)).awaken()
- }
-
- @Test
- fun testLaunchFullscreenIntent_updatesFsiInfoFlow() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, true)
- keyguardRepo.setKeyguardShowing(true)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- val expectedFsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi)
- assertEquals(expectedFsiInfo, fsiChromeRepo.infoFlow.value)
- }
-
- @Test
- fun testLaunchFullscreenIntent_notifyFsiLaunched() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, true)
- keyguardRepo.setKeyguardShowing(true)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- Mockito.verify(entry, times(1)).notifyFullScreenIntentLaunched()
- }
-
- @Test
- fun testLaunchFullscreenIntent_wakesUpDevice() {
- // Setup
- featureFlags.set(Flags.FSI_CHROME, true)
- keyguardRepo.setKeyguardShowing(true)
-
- // Test
- val entry = createFsiEntry(fsi)
- fsiChromeRepo.launchFullscreenIntent(entry)
-
- // Verify
- Mockito.verify(centralSurfaces, times(1)).wakeUpForFullScreenIntent()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt
deleted file mode 100644
index 5cee9e377dfb..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.fsi
-
-import android.app.PendingIntent
-import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.time.FakeSystemClock
-import com.android.wm.shell.TaskView
-import com.android.wm.shell.TaskViewFactory
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Consumer
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
-class FsiChromeViewModelFactoryTest : SysuiTestCase() {
- @Mock private lateinit var taskViewFactoryOptional: Optional<TaskViewFactory>
- @Mock private lateinit var taskViewFactory: TaskViewFactory
- @Mock lateinit var taskView: TaskView
-
- @Main var mainExecutor = FakeExecutor(FakeSystemClock())
- lateinit var viewModelFactory: FsiChromeViewModelFactory
-
- private val fakeInfoFlow = MutableStateFlow<FsiChromeRepo.FSIInfo?>(null)
- private var fsiChromeRepo: FsiChromeRepo =
- mock<FsiChromeRepo>().apply { whenever(infoFlow).thenReturn(fakeInfoFlow) }
-
- private val appName = "appName"
- private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android)
- private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java)
- private val fsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- whenever(taskViewFactoryOptional.get()).thenReturn(taskViewFactory)
-
- viewModelFactory =
- FsiChromeViewModelFactory(fsiChromeRepo, taskViewFactoryOptional, context, mainExecutor)
- }
-
- @Test
- fun testViewModelFlow_update_createsTaskView() {
- runTest {
- val latestViewModel =
- viewModelFactory.viewModelFlow
- .onStart { FsiDebug.log("viewModelFactory.viewModelFlow.onStart") }
- .stateIn(
- backgroundScope, // stateIn runs forever, don't count it as test coroutine
- SharingStarted.Eagerly,
- null
- )
- runCurrent() // Drain queued backgroundScope operations
-
- // Test: emit the fake FSIInfo
- fakeInfoFlow.emit(fsiInfo)
- runCurrent()
-
- val taskViewFactoryCallback: Consumer<TaskView> = withArgCaptor {
- verify(taskViewFactory).create(any(), any(), capture())
- }
- taskViewFactoryCallback.accept(taskView) // this will call k.resume
- runCurrent()
-
- // Verify that the factory has produced a new ViewModel
- // containing the relevant data from FsiInfo
- val expectedViewModel =
- FsiChromeViewModel(appName, appIcon, taskView, fsi, fsiChromeRepo)
-
- assertThat(latestViewModel.value).isEqualTo(expectedViewModel)
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index ec294b12a11a..68d67ca20007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -517,6 +517,26 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
verify(mVibratorHelper).vibrateAuthError(anyString());
}
+ @Test
+ public void onFingerprintDetect_showBouncer() {
+ // WHEN fingerprint detect occurs
+ mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
+
+ // THEN shows primary bouncer
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ }
+
+ @Test
+ public void onFaceDetect_showBouncer() {
+ // WHEN face detect occurs
+ mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE, false /* isStrongBiometric */);
+
+ // THEN shows primary bouncer
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ }
+
private void givenFingerprintModeUnlockCollapsing() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 38e82281856f..a1684320e219 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -77,6 +77,8 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewRootImpl;
import android.view.WindowManager;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
@@ -114,6 +116,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.notetask.NoteTaskController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
@@ -181,6 +184,8 @@ import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -193,8 +198,6 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Optional;
-import dagger.Lazy;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -259,6 +262,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private Bubbles mBubbles;
+ @Mock private NoteTaskController mNoteTaskController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -331,6 +335,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
+ // For the Shade to respond to Back gesture, we must enable the event routing
+ mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
+ // For the Shade to animate during the Back gesture, we must enable the animation flag.
+ mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -472,6 +480,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mWakefulnessLifecycle,
mStatusBarStateController,
Optional.of(mBubbles),
+ () -> mNoteTaskController,
mDeviceProvisionedController,
mNavigationBarController,
mAccessibilityFloatingMenuController,
@@ -852,6 +861,50 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
verify(mShadeController).animateCollapseShade();
}
+ /**
+ * When back progress is at 100%, the onBackProgressed animation driver inside
+ * NotificationPanelViewController should be invoked appropriately (with 1.0f passed in).
+ */
+ @Test
+ public void testPredictiveBackAnimation_progressMaxScalesPanel() {
+ mCentralSurfaces.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ OnBackAnimationCallback onBackAnimationCallback =
+ (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
+ when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+
+ BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT);
+ onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
+ verify(mNotificationPanelViewController).onBackProgressed(eq(1.0f));
+ }
+
+ /**
+ * When back progress is at 0%, the onBackProgressed animation driver inside
+ * NotificationPanelViewController should be invoked appropriately (with 0.0f passed in).
+ */
+ @Test
+ public void testPredictiveBackAnimation_progressMinScalesPanel() {
+ mCentralSurfaces.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ OnBackAnimationCallback onBackAnimationCallback =
+ (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
+ when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+
+ BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT);
+ onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
+ verify(mNotificationPanelViewController).onBackProgressed(eq(0.0f));
+ }
+
@Test
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c0537a6dc4cf..dc5a0472f49e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1356,33 +1356,10 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void notificationAlpha_unnocclusionAnimating_bouncerActive_usesKeyguardNotifAlpha() {
- when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
- mScrimController.setClipsQsScrim(true);
-
- mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.setUnocclusionAnimationRunning(true);
-
- assertAlphaAfterExpansion(
- mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f);
- assertAlphaAfterExpansion(
- mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0.4f);
- assertAlphaAfterExpansion(
- mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f);
-
- // Verify normal behavior after
- mScrimController.setUnocclusionAnimationRunning(false);
- float expansion = 0.4f;
- float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
- assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
- }
-
- @Test
public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() {
when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.setUnocclusionAnimationRunning(true);
assertAlphaAfterExpansion(
mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f);
@@ -1392,7 +1369,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f);
// Verify normal behavior after
- mScrimController.setUnocclusionAnimationRunning(false);
float expansion = 0.4f;
float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
@@ -1598,7 +1574,6 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void setUnOccludingAnimationKeyguard() {
- mScrimController.setUnocclusionAnimationRunning(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
finishAnimationsImmediately();
assertThat(mNotificationsScrim.getViewAlpha())
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 1aad83eb73ae..158e9adcff43 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
@@ -368,17 +368,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mCentralSurfaces).animateKeyguardUnoccluding();
-
- when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
- clearInvocations(mCentralSurfaces);
- mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
- }
-
- @Test
public void setOccluded_onKeyguardOccludedChangedCalled() {
clearInvocations(mKeyguardStateController);
clearInvocations(mKeyguardUpdateMonitor);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1f8a77965bf5..7fba72b74e8d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3801,6 +3801,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId)
throws RemoteException {
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
+ mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE);
if (client == null) {
return false;
}
@@ -3837,6 +3838,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public boolean unregisterProxyForDisplay(int displayId) {
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
+ mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE);
final long identity = Binder.clearCallingIdentity();
try {
return mProxyManager.unregisterProxy(displayId);
@@ -3928,6 +3930,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mGlobalClients.getRegisteredCallbackCookie(i);
pw.append(Arrays.toString(client.mPackageNames));
}
+ pw.println();
+ mProxyManager.dump(fd, pw, args);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 094053ed5396..0e25a06b4727 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -910,6 +910,10 @@ public class AccessibilityWindowManager {
pw.println(" Top Focused Window Id = " + mTopFocusedWindowId);
pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
+ " ]");
+ if (mIsProxy) {
+ pw.println("Proxy accessibility focused window = "
+ + mProxyDisplayAccessibilityFocusedWindow);
+ }
pw.println();
if (mWindows != null) {
final int windowCount = mWindows.size();
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index 945d43b94dac..b19a502547ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -38,14 +38,18 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityDisplayProxy;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import androidx.annotation.Nullable;
import com.android.internal.R;
+import com.android.internal.util.DumpUtils;
import com.android.server.wm.WindowManagerInternal;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -61,6 +65,8 @@ import java.util.Set;
* TODO(241429275): Initialize this when a proxy is registered.
*/
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
+ private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
+
private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
@@ -565,4 +571,25 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
public void setAnimationScale(float scale) throws UnsupportedOperationException {
throw new UnsupportedOperationException("setAnimationScale is not supported");
}
+
+ @Override
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
+ synchronized (mLock) {
+ pw.append("Proxy[displayId=" + mDisplayId);
+ pw.append(", feedbackType"
+ + AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
+ pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
+ pw.append(", eventTypes="
+ + AccessibilityEvent.eventTypeToString(mEventTypes));
+ pw.append(", notificationTimeout=" + mNotificationTimeout);
+ pw.append(", focusStrokeWidth=").append(String.valueOf(mFocusStrokeWidth));
+ pw.append(", focusColor=").append(String.valueOf(mFocusColor));
+ pw.append(", installedAndEnabledServiceCount=").append(String.valueOf(
+ mInstalledAndEnabledServices.size()));
+ pw.append(", installedAndEnabledServices=").append(
+ mInstalledAndEnabledServices.toString());
+ pw.append("]");
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index 9d91d10a3aa1..e258de16caf5 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -23,6 +23,7 @@ import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.accessibility.AccessibilityEvent;
@@ -30,6 +31,8 @@ import android.view.accessibility.AccessibilityManager;
import com.android.server.wm.WindowManagerInternal;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -42,6 +45,9 @@ import java.util.List;
* TODO(262244375): Add unit tests.
*/
public class ProxyManager {
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "ProxyManager";
+
// Names used to populate ComponentName and ResolveInfo in connection.mA11yServiceInfo and in
// the infos of connection.setInstalledAndEnabledServices
static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
@@ -79,6 +85,9 @@ public class ProxyManager {
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal) throws RemoteException {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Register proxy for display id: " + displayId);
+ }
// Set a default AccessibilityServiceInfo that is used before the proxy's info is
// populated. A proxy has the touch exploration and window capabilities.
@@ -134,6 +143,9 @@ public class ProxyManager {
if (mProxyA11yServiceConnections.contains(displayId)) {
mProxyA11yServiceConnections.remove(displayId);
removed = true;
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Unregister proxy for display id " + displayId);
+ }
}
}
if (removed) {
@@ -155,19 +167,25 @@ public class ProxyManager {
*/
public boolean isProxyed(int displayId) {
synchronized (mLock) {
- return mProxyA11yServiceConnections.contains(displayId);
+ final boolean tracked = mProxyA11yServiceConnections.contains(displayId);
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Tracking proxy display " + displayId + " : " + tracked);
+ }
+ return tracked;
}
}
/**
- * Sends AccessibilityEvents to all proxies.
- * {@link android.view.accessibility.AccessibilityDisplayProxy} will filter based on display.
- * TODO(b/250929565): Filtering should happen in the system, not in the proxy.
+ * Sends AccessibilityEvents to a proxy given the event's displayId.
*/
public void sendAccessibilityEventLocked(AccessibilityEvent event) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.get(event.getDisplayId());
if (proxy != null) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Send proxy event " + event + " for display id "
+ + event.getDisplayId());
+ }
proxy.notifyAccessibilityEvent(event);
}
}
@@ -186,6 +204,9 @@ public class ProxyManager {
break;
}
}
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "At least one proxy can retrieve windows: " + observingWindows);
+ }
return observingWindows;
}
@@ -205,6 +226,14 @@ public class ProxyManager {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
}
}
+
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Accessibility is enabled for all proxies: "
+ + ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0));
+ Slog.v(LOG_TAG, "Touch exploration is enabled for all proxies: "
+ + ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED)
+ != 0));
+ }
return clientState;
// TODO(b/254545943): When A11yManager is separated, include support for other properties.
}
@@ -234,6 +263,10 @@ public class ProxyManager {
mProxyA11yServiceConnections.valueAt(i);
relevantEventTypes |= proxy.getRelevantEventTypes();
}
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Relevant event types for all proxies: "
+ + AccessibilityEvent.eventTypeToString(relevantEventTypes));
+ }
return relevantEventTypes;
}
@@ -275,4 +308,25 @@ public class ProxyManager {
void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
mA11yInputFilter = filter;
}
+
+
+ /**
+ * Prints information belonging to each display that is controlled by an
+ * AccessibilityDisplayProxy.
+ */
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Proxy manager state:");
+ pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size());
+ pw.println(" Registered proxy connections:");
+ for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
+ final ProxyAccessibilityServiceConnection proxy =
+ mProxyA11yServiceConnections.valueAt(i);
+ if (proxy != null) {
+ proxy.dump(fd, pw, args);
+ }
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 9c84c048003d..f85ef43f99fe 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -44,6 +44,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
@@ -279,7 +281,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return mTempPointerProperties;
}
- private void transitionTo(State state) {
+ @VisibleForTesting
+ void transitionTo(State state) {
if (DEBUG_STATE_TRANSITIONS) {
Slog.i(mLogTag,
(State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
@@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
.replace(getClass().getName(), ""));
}
mPreviousState = mCurrentState;
+ if (state == mPanningScalingState) {
+ mPanningScalingState.prepareForState();
+ }
mCurrentState = state;
}
@@ -317,18 +323,34 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
final class PanningScalingState extends SimpleOnGestureListener
implements OnScaleGestureListener, State {
+ private final Context mContext;
private final ScaleGestureDetector mScaleGestureDetector;
private final GestureDetector mScrollGestureDetector;
final float mScalingThreshold;
float mInitialScaleFactor = -1;
- boolean mScaling;
+ @VisibleForTesting boolean mScaling;
+
+ /**
+ * Whether it needs to detect the target scale passes
+ * {@link FullScreenMagnificationController#getPersistedScale} during panning scale.
+ */
+ @VisibleForTesting boolean mDetectingPassPersistedScale;
+
+ // The threshold for relative difference from given scale to persisted scale. If the
+ // difference >= threshold, we can start detecting if the scale passes the persisted
+ // scale during panning.
+ @VisibleForTesting static final float CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD = 0.2f;
+ // The threshold for relative difference from given scale to persisted scale. If the
+ // difference < threshold, we can decide that the scale passes the persisted scale.
+ @VisibleForTesting static final float PASSING_PERSISTED_SCALE_THRESHOLD = 0.01f;
PanningScalingState(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
R.dimen.config_screen_magnification_scaling_threshold,
scaleValue, false);
+ mContext = context;
mScalingThreshold = scaleValue.getFloat();
mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
mScaleGestureDetector.setQuickScaleEnabled(false);
@@ -351,12 +373,59 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+
+ void prepareForState() {
+ checkShouldDetectPassPersistedScale();
+ }
+
+ private void checkShouldDetectPassPersistedScale() {
+ if (mDetectingPassPersistedScale) {
+ return;
+ }
+
+ final float currentScale =
+ mFullScreenMagnificationController.getScale(mDisplayId);
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+
+ mDetectingPassPersistedScale =
+ (abs(currentScale - persistedScale) / persistedScale)
+ >= CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ }
+
public void persistScaleAndTransitionTo(State state) {
mFullScreenMagnificationController.persistScale(mDisplayId);
clear();
transitionTo(state);
}
+ @VisibleForTesting
+ void setScaleAndClearIfNeeded(float scale, float pivotX, float pivotY) {
+ if (mDetectingPassPersistedScale) {
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+ // If the scale passes the persisted scale during panning, perform a vibration
+ // feedback to user. Also, call {@link clear} to create a buffer zone so that
+ // user needs to panning more than {@link mScalingThreshold} to change scale again.
+ if (abs(scale - persistedScale) / persistedScale
+ < PASSING_PERSISTED_SCALE_THRESHOLD) {
+ scale = persistedScale;
+ final Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+ }
+ clear();
+ }
+ }
+
+ if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
+ mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ checkShouldDetectPassPersistedScale();
+ }
+
@Override
public boolean onScroll(MotionEvent first, MotionEvent second,
float distanceX, float distanceY) {
@@ -402,11 +471,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
scale = targetScale;
}
- final float pivotX = detector.getFocusX();
- final float pivotY = detector.getFocusY();
- if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
- mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ setScaleAndClearIfNeeded(scale, detector.getFocusX(), detector.getFocusY());
return /* handled: */ true;
}
@@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
public void clear() {
mInitialScaleFactor = -1;
mScaling = false;
+ mDetectingPassPersistedScale = false;
}
@Override
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 494c5a6c0779..6a53adfeea9d 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -18,51 +18,31 @@ package com.android.server.companion.transport;
import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
+import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.Build;
import android.os.ParcelFileDescriptor;
-import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
-import com.android.server.companion.securechannel.SecureChannel;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-import libcore.util.EmptyArray;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicInteger;
@SuppressLint("LongLogTag")
public class CompanionTransportManager {
private static final String TAG = "CDM_CompanionTransportManager";
- // TODO: flip to false
- private static final boolean DEBUG = true;
-
- private static final int HEADER_LENGTH = 12;
-
- private static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
- private static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
-
- private static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
- private static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI
+ private static final boolean DEBUG = false;
private boolean mSecureTransportEnabled = true;
@@ -127,9 +107,9 @@ public class CompanionTransportManager {
final Transport transport;
if (isSecureTransportEnabled(associationId)) {
- transport = new SecureTransport(associationId, fd);
+ transport = new SecureTransport(associationId, fd, mContext, mListener);
} else {
- transport = new RawTransport(associationId, fd);
+ transport = new RawTransport(associationId, fd, mContext, mListener);
}
transport.start();
@@ -172,296 +152,4 @@ public class CompanionTransportManager {
// TODO: version comparison logic
return enabled;
}
-
- // TODO: Make Transport inner classes into standalone classes.
- private abstract class Transport {
- protected final int mAssociationId;
- protected final InputStream mRemoteIn;
- protected final OutputStream mRemoteOut;
-
- @GuardedBy("mPendingRequests")
- protected final SparseArray<CompletableFuture<byte[]>> mPendingRequests =
- new SparseArray<>();
- protected final AtomicInteger mNextSequence = new AtomicInteger();
-
- Transport(int associationId, ParcelFileDescriptor fd) {
- this(associationId,
- new ParcelFileDescriptor.AutoCloseInputStream(fd),
- new ParcelFileDescriptor.AutoCloseOutputStream(fd));
- }
-
- Transport(int associationId, InputStream in, OutputStream out) {
- this.mAssociationId = associationId;
- this.mRemoteIn = in;
- this.mRemoteOut = out;
- }
-
- public abstract void start();
- public abstract void stop();
-
- protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
- throws IOException;
-
- public Future<byte[]> requestForResponse(int message, byte[] data) {
- if (DEBUG) Slog.d(TAG, "Requesting for response");
- final int sequence = mNextSequence.incrementAndGet();
- final CompletableFuture<byte[]> pending = new CompletableFuture<>();
- synchronized (mPendingRequests) {
- mPendingRequests.put(sequence, pending);
- }
-
- try {
- sendMessage(message, sequence, data);
- } catch (IOException e) {
- synchronized (mPendingRequests) {
- mPendingRequests.remove(sequence);
- }
- pending.completeExceptionally(e);
- }
-
- return pending;
- }
-
- protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
- throws IOException {
- if (DEBUG) {
- Slog.d(TAG, "Received message 0x" + Integer.toHexString(message)
- + " sequence " + sequence + " length " + data.length
- + " from association " + mAssociationId);
- }
-
- if (isRequest(message)) {
- try {
- processRequest(message, sequence, data);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to respond to 0x" + Integer.toHexString(message), e);
- }
- } else if (isResponse(message)) {
- processResponse(message, sequence, data);
- } else {
- Slog.w(TAG, "Unknown message 0x" + Integer.toHexString(message));
- }
- }
-
- private void processRequest(int message, int sequence, byte[] data)
- throws IOException {
- switch (message) {
- case MESSAGE_REQUEST_PING: {
- sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
- break;
- }
- case MESSAGE_REQUEST_PERMISSION_RESTORE: {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
- && !Build.isDebuggable()) {
- Slog.w(TAG, "Restoring permissions only supported on watches");
- sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
- break;
- }
- try {
- mListener.onRequestPermissionRestore(data);
- sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
- } catch (Exception e) {
- Slog.w(TAG, "Failed to restore permissions");
- sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
- }
- break;
- }
- default: {
- Slog.w(TAG, "Unknown request 0x" + Integer.toHexString(message));
- sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
- break;
- }
- }
- }
-
- private void processResponse(int message, int sequence, byte[] data) {
- final CompletableFuture<byte[]> future;
- synchronized (mPendingRequests) {
- future = mPendingRequests.removeReturnOld(sequence);
- }
- if (future == null) {
- Slog.w(TAG, "Ignoring unknown sequence " + sequence);
- return;
- }
-
- switch (message) {
- case MESSAGE_RESPONSE_SUCCESS: {
- future.complete(data);
- break;
- }
- case MESSAGE_RESPONSE_FAILURE: {
- future.completeExceptionally(new RuntimeException("Remote failure"));
- break;
- }
- default: {
- Slog.w(TAG, "Ignoring unknown response 0x" + Integer.toHexString(message));
- }
- }
- }
- }
-
- private class RawTransport extends Transport {
- private volatile boolean mStopped;
-
- RawTransport(int associationId, ParcelFileDescriptor fd) {
- super(associationId, fd);
- }
-
- @Override
- public void start() {
- new Thread(() -> {
- try {
- while (!mStopped) {
- receiveMessage();
- }
- } catch (IOException e) {
- if (!mStopped) {
- Slog.w(TAG, "Trouble during transport", e);
- stop();
- }
- }
- }).start();
- }
-
- @Override
- public void stop() {
- mStopped = true;
-
- IoUtils.closeQuietly(mRemoteIn);
- IoUtils.closeQuietly(mRemoteOut);
- }
-
- @Override
- protected void sendMessage(int message, int sequence, @NonNull byte[] data)
- throws IOException {
- if (DEBUG) {
- Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message)
- + " sequence " + sequence + " length " + data.length
- + " to association " + mAssociationId);
- }
-
- synchronized (mRemoteOut) {
- final ByteBuffer header = ByteBuffer.allocate(HEADER_LENGTH)
- .putInt(message)
- .putInt(sequence)
- .putInt(data.length);
- mRemoteOut.write(header.array());
- mRemoteOut.write(data);
- mRemoteOut.flush();
- }
- }
-
- private void receiveMessage() throws IOException {
- final byte[] headerBytes = new byte[HEADER_LENGTH];
- Streams.readFully(mRemoteIn, headerBytes);
- final ByteBuffer header = ByteBuffer.wrap(headerBytes);
- final int message = header.getInt();
- final int sequence = header.getInt();
- final int length = header.getInt();
- final byte[] data = new byte[length];
- Streams.readFully(mRemoteIn, data);
-
- handleMessage(message, sequence, data);
- }
- }
-
- private class SecureTransport extends Transport implements SecureChannel.Callback {
- private final SecureChannel mSecureChannel;
-
- private volatile boolean mShouldProcessRequests = false;
-
- private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100);
-
- SecureTransport(int associationId, ParcelFileDescriptor fd) {
- super(associationId, fd);
- mSecureChannel = new SecureChannel(mRemoteIn, mRemoteOut, this, mContext);
- }
-
- @Override
- public void start() {
- mSecureChannel.start();
- }
-
- @Override
- public void stop() {
- mSecureChannel.stop();
- mShouldProcessRequests = false;
- }
-
- @Override
- public Future<byte[]> requestForResponse(int message, byte[] data) {
- // Check if channel is secured and start securing
- if (!mShouldProcessRequests) {
- Slog.d(TAG, "Establishing secure connection.");
- try {
- mSecureChannel.establishSecureConnection();
- } catch (Exception e) {
- Slog.w(TAG, "Failed to initiate secure channel handshake.", e);
- onError(e);
- }
- }
-
- return super.requestForResponse(message, data);
- }
-
- @Override
- protected void sendMessage(int message, int sequence, @NonNull byte[] data)
- throws IOException {
- if (DEBUG) {
- Slog.d(TAG, "Queueing message 0x" + Integer.toHexString(message)
- + " sequence " + sequence + " length " + data.length
- + " to association " + mAssociationId);
- }
-
- // Queue up a message to send
- mRequestQueue.add(ByteBuffer.allocate(HEADER_LENGTH + data.length)
- .putInt(message)
- .putInt(sequence)
- .putInt(data.length)
- .put(data)
- .array());
- }
-
- @Override
- public void onSecureConnection() {
- mShouldProcessRequests = true;
- Slog.d(TAG, "Secure connection established.");
-
- // TODO: find a better way to handle incoming requests than a dedicated thread.
- new Thread(() -> {
- try {
- while (mShouldProcessRequests) {
- byte[] request = mRequestQueue.poll();
- if (request != null) {
- mSecureChannel.sendSecureMessage(request);
- }
- }
- } catch (IOException e) {
- onError(e);
- }
- }).start();
- }
-
- @Override
- public void onSecureMessageReceived(byte[] data) {
- final ByteBuffer payload = ByteBuffer.wrap(data);
- final int message = payload.getInt();
- final int sequence = payload.getInt();
- final int length = payload.getInt();
- final byte[] content = new byte[length];
- payload.get(content);
-
- try {
- handleMessage(message, sequence, content);
- } catch (IOException error) {
- onError(error);
- }
- }
-
- @Override
- public void onError(Throwable error) {
- mShouldProcessRequests = false;
- Slog.e(TAG, error.getMessage(), error);
- }
- }
}
diff --git a/services/companion/java/com/android/server/companion/transport/RawTransport.java b/services/companion/java/com/android/server/companion/transport/RawTransport.java
new file mode 100644
index 000000000000..7c0c7cf7ac68
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/transport/RawTransport.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.transport;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Slog;
+
+import com.android.server.companion.transport.CompanionTransportManager.Listener;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+class RawTransport extends Transport {
+ private volatile boolean mStopped;
+
+ RawTransport(int associationId, ParcelFileDescriptor fd, Context context, Listener listener) {
+ super(associationId, fd, context, listener);
+ }
+
+ @Override
+ public void start() {
+ new Thread(() -> {
+ try {
+ while (!mStopped) {
+ receiveMessage();
+ }
+ } catch (IOException e) {
+ if (!mStopped) {
+ Slog.w(TAG, "Trouble during transport", e);
+ stop();
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public void stop() {
+ mStopped = true;
+
+ IoUtils.closeQuietly(mRemoteIn);
+ IoUtils.closeQuietly(mRemoteOut);
+ }
+
+ @Override
+ protected void sendMessage(int message, int sequence, @NonNull byte[] data)
+ throws IOException {
+ if (DEBUG) {
+ Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message)
+ + " sequence " + sequence + " length " + data.length
+ + " to association " + mAssociationId);
+ }
+
+ synchronized (mRemoteOut) {
+ final ByteBuffer header = ByteBuffer.allocate(HEADER_LENGTH)
+ .putInt(message)
+ .putInt(sequence)
+ .putInt(data.length);
+ mRemoteOut.write(header.array());
+ mRemoteOut.write(data);
+ mRemoteOut.flush();
+ }
+ }
+
+ private void receiveMessage() throws IOException {
+ final byte[] headerBytes = new byte[HEADER_LENGTH];
+ Streams.readFully(mRemoteIn, headerBytes);
+ final ByteBuffer header = ByteBuffer.wrap(headerBytes);
+ final int message = header.getInt();
+ final int sequence = header.getInt();
+ final int length = header.getInt();
+ final byte[] data = new byte[length];
+ Streams.readFully(mRemoteIn, data);
+
+ handleMessage(message, sequence, data);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
new file mode 100644
index 000000000000..4194130f7e84
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.transport;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Slog;
+
+import com.android.server.companion.securechannel.SecureChannel;
+import com.android.server.companion.transport.CompanionTransportManager.Listener;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Future;
+
+class SecureTransport extends Transport implements SecureChannel.Callback {
+ private final SecureChannel mSecureChannel;
+
+ private volatile boolean mShouldProcessRequests = false;
+
+ private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100);
+
+ SecureTransport(int associationId,
+ ParcelFileDescriptor fd,
+ Context context,
+ Listener listener) {
+ super(associationId, fd, context, listener);
+ mSecureChannel = new SecureChannel(mRemoteIn, mRemoteOut, this, context);
+ }
+
+ @Override
+ public void start() {
+ mSecureChannel.start();
+ }
+
+ @Override
+ public void stop() {
+ mSecureChannel.stop();
+ mShouldProcessRequests = false;
+ }
+
+ @Override
+ public Future<byte[]> requestForResponse(int message, byte[] data) {
+ // Check if channel is secured and start securing
+ if (!mShouldProcessRequests) {
+ Slog.d(TAG, "Establishing secure connection.");
+ try {
+ mSecureChannel.establishSecureConnection();
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to initiate secure channel handshake.", e);
+ onError(e);
+ }
+ }
+
+ return super.requestForResponse(message, data);
+ }
+
+ @Override
+ protected void sendMessage(int message, int sequence, @NonNull byte[] data)
+ throws IOException {
+ if (DEBUG) {
+ Slog.d(TAG, "Queueing message 0x" + Integer.toHexString(message)
+ + " sequence " + sequence + " length " + data.length
+ + " to association " + mAssociationId);
+ }
+
+ // Queue up a message to send
+ mRequestQueue.add(ByteBuffer.allocate(HEADER_LENGTH + data.length)
+ .putInt(message)
+ .putInt(sequence)
+ .putInt(data.length)
+ .put(data)
+ .array());
+ }
+
+ @Override
+ public void onSecureConnection() {
+ mShouldProcessRequests = true;
+ Slog.d(TAG, "Secure connection established.");
+
+ // TODO: find a better way to handle incoming requests than a dedicated thread.
+ new Thread(() -> {
+ try {
+ while (mShouldProcessRequests) {
+ byte[] request = mRequestQueue.poll();
+ if (request != null) {
+ mSecureChannel.sendSecureMessage(request);
+ }
+ }
+ } catch (IOException e) {
+ onError(e);
+ }
+ }).start();
+ }
+
+ @Override
+ public void onSecureMessageReceived(byte[] data) {
+ final ByteBuffer payload = ByteBuffer.wrap(data);
+ final int message = payload.getInt();
+ final int sequence = payload.getInt();
+ final int length = payload.getInt();
+ final byte[] content = new byte[length];
+ payload.get(content);
+
+ try {
+ handleMessage(message, sequence, content);
+ } catch (IOException error) {
+ onError(error);
+ }
+ }
+
+ @Override
+ public void onError(Throwable error) {
+ mShouldProcessRequests = false;
+ Slog.e(TAG, error.getMessage(), error);
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
new file mode 100644
index 000000000000..923d4243a34c
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.transport;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.companion.transport.CompanionTransportManager.Listener;
+
+import libcore.util.EmptyArray;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+abstract class Transport {
+ protected static final String TAG = "CDM_CompanionTransport";
+ protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
+
+ static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
+ static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
+
+ static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
+ static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI
+
+ protected static final int HEADER_LENGTH = 12;
+
+ protected final int mAssociationId;
+ protected final InputStream mRemoteIn;
+ protected final OutputStream mRemoteOut;
+ protected final Context mContext;
+
+ private final Listener mListener;
+
+ private static boolean isRequest(int message) {
+ return (message & 0xFF000000) == 0x63000000;
+ }
+
+ private static boolean isResponse(int message) {
+ return (message & 0xFF000000) == 0x33000000;
+ }
+
+ @GuardedBy("mPendingRequests")
+ protected final SparseArray<CompletableFuture<byte[]>> mPendingRequests =
+ new SparseArray<>();
+ protected final AtomicInteger mNextSequence = new AtomicInteger();
+
+ Transport(int associationId, ParcelFileDescriptor fd, Context context, Listener listener) {
+ this.mAssociationId = associationId;
+ this.mRemoteIn = new ParcelFileDescriptor.AutoCloseInputStream(fd);
+ this.mRemoteOut = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ this.mContext = context;
+ this.mListener = listener;
+ }
+
+ public abstract void start();
+ public abstract void stop();
+
+ public Future<byte[]> requestForResponse(int message, byte[] data) {
+ if (DEBUG) Slog.d(TAG, "Requesting for response");
+ final int sequence = mNextSequence.incrementAndGet();
+ final CompletableFuture<byte[]> pending = new CompletableFuture<>();
+ synchronized (mPendingRequests) {
+ mPendingRequests.put(sequence, pending);
+ }
+
+ try {
+ sendMessage(message, sequence, data);
+ } catch (IOException e) {
+ synchronized (mPendingRequests) {
+ mPendingRequests.remove(sequence);
+ }
+ pending.completeExceptionally(e);
+ }
+
+ return pending;
+ }
+
+ protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
+ throws IOException;
+
+ protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
+ throws IOException {
+ if (DEBUG) {
+ Slog.d(TAG, "Received message 0x" + Integer.toHexString(message)
+ + " sequence " + sequence + " length " + data.length
+ + " from association " + mAssociationId);
+ }
+
+ if (isRequest(message)) {
+ try {
+ processRequest(message, sequence, data);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to respond to 0x" + Integer.toHexString(message), e);
+ }
+ } else if (isResponse(message)) {
+ processResponse(message, sequence, data);
+ } else {
+ Slog.w(TAG, "Unknown message 0x" + Integer.toHexString(message));
+ }
+ }
+
+ private void processRequest(int message, int sequence, byte[] data)
+ throws IOException {
+ switch (message) {
+ case MESSAGE_REQUEST_PING: {
+ sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
+ break;
+ }
+ case MESSAGE_REQUEST_PERMISSION_RESTORE: {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ && !Build.isDebuggable()) {
+ Slog.w(TAG, "Restoring permissions only supported on watches");
+ sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
+ break;
+ }
+ try {
+ mListener.onRequestPermissionRestore(data);
+ sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to restore permissions");
+ sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
+ }
+ break;
+ }
+ default: {
+ Slog.w(TAG, "Unknown request 0x" + Integer.toHexString(message));
+ sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE);
+ break;
+ }
+ }
+ }
+
+ private void processResponse(int message, int sequence, byte[] data) {
+ final CompletableFuture<byte[]> future;
+ synchronized (mPendingRequests) {
+ future = mPendingRequests.removeReturnOld(sequence);
+ }
+ if (future == null) {
+ Slog.w(TAG, "Ignoring unknown sequence " + sequence);
+ return;
+ }
+
+ switch (message) {
+ case MESSAGE_RESPONSE_SUCCESS: {
+ future.complete(data);
+ break;
+ }
+ case MESSAGE_RESPONSE_FAILURE: {
+ future.completeExceptionally(new RuntimeException("Remote failure"));
+ break;
+ }
+ default: {
+ Slog.w(TAG, "Ignoring unknown response 0x" + Integer.toHexString(message));
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 7804ebf1583d..864fe0f5edc1 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -22,8 +22,11 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.hardware.SensorDirectChannel;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SharedMemory;
import android.util.ArrayMap;
import android.util.Slog;
@@ -36,6 +39,7 @@ import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/** Controls virtual sensors, including their lifecycle and sensor event dispatch. */
public class SensorController {
@@ -47,6 +51,8 @@ public class SensorController {
private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value
private static final int BAD_VALUE = -22;
+ private static AtomicInteger sNextDirectChannelHandle = new AtomicInteger(1);
+
private final Object mLock;
private final int mVirtualDeviceId;
@GuardedBy("mLock")
@@ -57,8 +63,6 @@ public class SensorController {
private final SensorManagerInternal mSensorManagerInternal;
private final VirtualDeviceManagerInternal mVdmInternal;
-
-
public SensorController(@NonNull Object lock, int virtualDeviceId,
@Nullable IVirtualSensorCallback virtualSensorCallback) {
mLock = lock;
@@ -97,7 +101,7 @@ public class SensorController {
throws SensorCreationException {
final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId,
config.getType(), config.getName(),
- config.getVendor() == null ? "" : config.getVendor(),
+ config.getVendor() == null ? "" : config.getVendor(), config.getFlags(),
mRuntimeSensorCallback);
if (handle <= 0) {
throw new SensorCreationException("Received an invalid virtual sensor handle.");
@@ -212,6 +216,66 @@ public class SensorController {
}
return OK;
}
+
+ @Override
+ public int onDirectChannelCreated(ParcelFileDescriptor fd) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId);
+ return BAD_VALUE;
+ } else if (fd == null) {
+ Slog.e(TAG, "Received invalid ParcelFileDescriptor");
+ return BAD_VALUE;
+ }
+ final int channelHandle = sNextDirectChannelHandle.getAndIncrement();
+ SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd);
+ try {
+ mCallback.onDirectChannelCreated(channelHandle, sharedMemory);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ return UNKNOWN_ERROR;
+ }
+ return channelHandle;
+ }
+
+ @Override
+ public void onDirectChannelDestroyed(int channelHandle) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId);
+ return;
+ }
+ try {
+ mCallback.onDirectChannelDestroyed(channelHandle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ }
+ }
+
+ @Override
+ public int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ @SensorDirectChannel.RateLevel int rateLevel) {
+ if (mCallback == null) {
+ Slog.e(TAG, "No runtime sensor callback configured.");
+ return BAD_VALUE;
+ }
+ VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, sensorHandle);
+ if (sensor == null) {
+ Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId
+ + " and sensor handle=" + sensorHandle);
+ return BAD_VALUE;
+ }
+ try {
+ mCallback.onDirectChannelConfigured(channelHandle, sensor, rateLevel, sensorHandle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call sensor callback: " + e);
+ return UNKNOWN_ERROR;
+ }
+ if (rateLevel == SensorDirectChannel.RATE_STOP) {
+ return OK;
+ } else {
+ // Use the sensor handle as a report token, i.e. a unique identifier of the sensor.
+ return sensorHandle;
+ }
+ }
}
@VisibleForTesting
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 b4dcf43c2e1a..642eaef80c47 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -51,6 +51,10 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualKeyboardConfig;
@@ -82,6 +86,7 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.server.LocalServices;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.VirtualAudioController;
@@ -100,6 +105,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private static final String TAG = "VirtualDeviceImpl";
+ private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
+
/**
* Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched.
*/
@@ -109,21 +122,29 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final Context mContext;
private final AssociationInfo mAssociationInfo;
+ private final VirtualDeviceManagerService mService;
private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
private final int mDeviceId;
+ // Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
+ // Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
+ // 1. After display is created the window manager calls into VDM during construction
+ // of display specific context to fetch device id corresponding to the display.
+ // mVirtualDeviceLock will be held while this is done.
+ // 2. InputController interactions result in calls to DisplayManager (to set IME,
+ // possibly more indirect calls), and those attempt to lock GlobalWindowManagerLock which
+ // creates lock inversion.
private final InputController mInputController;
private final SensorController mSensorController;
private final CameraAccessController mCameraAccessController;
private VirtualAudioController mVirtualAudioController;
- @VisibleForTesting
- final ArraySet<Integer> mVirtualDisplayIds = new ArraySet<>();
- private final OnDeviceCloseListener mOnDeviceCloseListener;
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
- private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
+ @GuardedBy("mVirtualDeviceLock")
+ private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
private final IVirtualDeviceActivityListener mActivityListener;
private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
+ private final DisplayManagerGlobal mDisplayManager;
@GuardedBy("mVirtualDeviceLock")
private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
@NonNull
@@ -174,21 +195,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
};
}
- /**
- * A mapping from the virtual display ID to its corresponding
- * {@link GenericWindowPolicyController}.
- */
- private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers =
- new SparseArray<>();
-
VirtualDeviceImpl(
Context context,
AssociationInfo associationInfo,
+ VirtualDeviceManagerService service,
IBinder token,
int ownerUid,
int deviceId,
CameraAccessController cameraAccessController,
- OnDeviceCloseListener onDeviceCloseListener,
PendingTrampolineCallback pendingTrampolineCallback,
IVirtualDeviceActivityListener activityListener,
IVirtualDeviceSoundEffectListener soundEffectListener,
@@ -197,40 +211,43 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
this(
context,
associationInfo,
+ service,
token,
ownerUid,
deviceId,
/* inputController= */ null,
/* sensorController= */ null,
cameraAccessController,
- onDeviceCloseListener,
pendingTrampolineCallback,
activityListener,
soundEffectListener,
runningAppsChangedCallback,
- params);
+ params,
+ DisplayManagerGlobal.getInstance());
}
@VisibleForTesting
VirtualDeviceImpl(
Context context,
AssociationInfo associationInfo,
+ VirtualDeviceManagerService service,
IBinder token,
int ownerUid,
int deviceId,
InputController inputController,
SensorController sensorController,
CameraAccessController cameraAccessController,
- OnDeviceCloseListener onDeviceCloseListener,
PendingTrampolineCallback pendingTrampolineCallback,
IVirtualDeviceActivityListener activityListener,
IVirtualDeviceSoundEffectListener soundEffectListener,
Consumer<ArraySet<Integer>> runningAppsChangedCallback,
- VirtualDeviceParams params) {
+ VirtualDeviceParams params,
+ DisplayManagerGlobal displayManager) {
super(PermissionEnforcer.fromContext(context));
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
+ mService = service;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
mSoundEffectListener = soundEffectListener;
@@ -239,6 +256,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mDeviceId = deviceId;
mAppToken = token;
mParams = params;
+ mDisplayManager = displayManager;
if (inputController == null) {
mInputController = new InputController(
mVirtualDeviceLock,
@@ -259,7 +277,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
mCameraAccessController = cameraAccessController;
mCameraAccessController.startObservingIfNeeded();
- mOnDeviceCloseListener = onDeviceCloseListener;
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -272,7 +289,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
* device.
*/
int getBaseVirtualDisplayFlags() {
- int flags = 0;
+ int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
}
@@ -331,9 +348,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
ResultReceiver resultReceiver) {
- if (!mVirtualDisplayIds.contains(displayId)) {
- throw new SecurityException("Display ID " + displayId
- + " not found for this virtual device");
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplays.contains(displayId)) {
+ throw new SecurityException("Display ID " + displayId
+ + " not found for this virtual device");
+ }
}
if (pendingIntent.isActivity()) {
try {
@@ -383,24 +402,34 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void close() {
super.close_enforcePermission();
+ // Remove about-to-be-closed virtual device from the service before butchering it.
+ mService.removeVirtualDevice(mDeviceId);
+
+ VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
synchronized (mVirtualDeviceLock) {
- if (!mPerDisplayWakelocks.isEmpty()) {
- mPerDisplayWakelocks.forEach((displayId, wakeLock) -> {
- Slog.w(TAG, "VirtualDisplay " + displayId + " owned by UID " + mOwnerUid
- + " was not properly released");
- wakeLock.release();
- });
- mPerDisplayWakelocks.clear();
- }
if (mVirtualAudioController != null) {
mVirtualAudioController.stopListening();
mVirtualAudioController = null;
}
mLocaleList = null;
+ virtualDisplaysToBeReleased = new VirtualDisplayWrapper[mVirtualDisplays.size()];
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ virtualDisplaysToBeReleased[i] = mVirtualDisplays.valueAt(i);
+ }
+ mVirtualDisplays.clear();
mVirtualSensorList = null;
mVirtualSensors.clear();
}
- mOnDeviceCloseListener.onClose(mDeviceId);
+ // Destroy the display outside locked section.
+ for (VirtualDisplayWrapper virtualDisplayWrapper : virtualDisplaysToBeReleased) {
+ mDisplayManager.releaseVirtualDisplay(virtualDisplayWrapper.getToken());
+ // The releaseVirtualDisplay call above won't trigger
+ // VirtualDeviceImpl.onVirtualDisplayRemoved callback because we already removed the
+ // virtual device from the service - we release the other display-tied resources here
+ // with the guarantee it will be done exactly once.
+ releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+ }
+
mAppToken.unlinkToDeath(this, 0);
mCameraAccessController.stopObservingIfNeeded();
@@ -429,11 +458,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return mVirtualAudioController;
}
- @VisibleForTesting
- SparseArray<GenericWindowPolicyController> getWindowPolicyControllersForTesting() {
- return mWindowPolicyControllers;
- }
-
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void onAudioSessionStarting(int displayId,
@@ -441,7 +465,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Nullable IAudioConfigChangedCallback configChangedCallback) {
super.onAudioSessionStarting_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(displayId)) {
+ if (!mVirtualDisplays.contains(displayId)) {
throw new SecurityException(
"Cannot start audio session for a display not associated with this virtual "
+ "device");
@@ -449,7 +473,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (mVirtualAudioController == null) {
mVirtualAudioController = new VirtualAudioController(mContext);
- GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId);
+ GenericWindowPolicyController gwpc = mVirtualDisplays.get(
+ displayId).getWindowPolicyController();
mVirtualAudioController.startListening(gwpc, routingCallback,
configChangedCallback);
}
@@ -473,7 +498,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
super.createVirtualDpad_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+ if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
"Cannot create a virtual dpad for a display not associated with "
+ "this virtual device");
@@ -493,7 +518,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) {
super.createVirtualKeyboard_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+ if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
"Cannot create a virtual keyboard for a display not associated with "
+ "this virtual device");
@@ -515,7 +540,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) {
super.createVirtualMouse_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+ if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
"Cannot create a virtual mouse for a display not associated with this "
+ "virtual device");
@@ -536,7 +561,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@NonNull IBinder deviceToken) {
super.createVirtualTouchscreen_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+ if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
"Cannot create a virtual touchscreen for a display not associated with "
+ "this virtual device");
@@ -566,7 +591,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@NonNull IBinder deviceToken) {
super.createVirtualNavigationTouchpad_enforcePermission();
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+ if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
"Cannot create a virtual navigation touchpad for a display not associated "
+ "with this virtual device");
@@ -704,7 +729,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
try {
synchronized (mVirtualDeviceLock) {
mDefaultShowPointerIcon = showPointerIcon;
- for (int displayId : mVirtualDisplayIds) {
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ final int displayId = mVirtualDisplays.keyAt(i);
mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
}
}
@@ -795,8 +821,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
fout.println(" mParams: " + mParams);
fout.println(" mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
- for (int id : mVirtualDisplayIds) {
- fout.println(" " + id);
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ fout.println(" " + mVirtualDisplays.keyAt(i));
}
fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
}
@@ -804,61 +830,75 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mSensorController.dump(fout);
}
- GenericWindowPolicyController createWindowPolicyController(
+ private GenericWindowPolicyController createWindowPolicyController(
@NonNull List<String> displayCategories) {
- synchronized (mVirtualDeviceLock) {
- final GenericWindowPolicyController gwpc =
- new GenericWindowPolicyController(FLAG_SECURE,
- SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
- getAllowedUserHandles(),
- mParams.getAllowedCrossTaskNavigations(),
- mParams.getBlockedCrossTaskNavigations(),
- mParams.getAllowedActivities(),
- mParams.getBlockedActivities(),
- mParams.getDefaultActivityPolicy(),
- createListenerAdapter(),
- this::onEnteringPipBlocked,
- this::onActivityBlocked,
- this::onSecureWindowShown,
- this::shouldInterceptIntent,
- displayCategories,
- mParams.getDefaultRecentsPolicy());
- gwpc.registerRunningAppsChangedListener(/* listener= */ this);
- return gwpc;
- }
+ final GenericWindowPolicyController gwpc =
+ new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ getAllowedUserHandles(),
+ mParams.getAllowedCrossTaskNavigations(),
+ mParams.getBlockedCrossTaskNavigations(),
+ mParams.getAllowedActivities(),
+ mParams.getBlockedActivities(),
+ mParams.getDefaultActivityPolicy(),
+ createListenerAdapter(),
+ this::onEnteringPipBlocked,
+ this::onActivityBlocked,
+ this::onSecureWindowShown,
+ this::shouldInterceptIntent,
+ displayCategories,
+ mParams.getDefaultRecentsPolicy());
+ gwpc.registerRunningAppsChangedListener(/* listener= */ this);
+ return gwpc;
}
- void onVirtualDisplayCreatedLocked(GenericWindowPolicyController gwpc, int displayId) {
+ int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
+ @NonNull IVirtualDisplayCallback callback, String packageName) {
+ GenericWindowPolicyController gwpc = createWindowPolicyController(
+ virtualDisplayConfig.getDisplayCategories());
+ DisplayManagerInternal displayManager = LocalServices.getService(
+ DisplayManagerInternal.class);
+ int displayId;
+ displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
+ this, gwpc, packageName);
+ gwpc.setDisplayId(displayId);
+
synchronized (mVirtualDeviceLock) {
- if (displayId == Display.INVALID_DISPLAY) {
- return;
- }
- if (mVirtualDisplayIds.contains(displayId)) {
+ if (mVirtualDisplays.contains(displayId)) {
+ gwpc.unregisterRunningAppsChangedListener(this);
throw new IllegalStateException(
"Virtual device already has a virtual display with ID " + displayId);
}
- mVirtualDisplayIds.add(displayId);
- gwpc.setDisplayId(displayId);
- mWindowPolicyControllers.put(displayId, gwpc);
+ PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId);
+ mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock));
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
mInputController.setPointerAcceleration(1f, displayId);
mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
displayId);
mInputController.setLocalIme(displayId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return displayId;
+ }
- if (mPerDisplayWakelocks.containsKey(displayId)) {
- Slog.e(TAG, "Not creating wakelock for displayId " + displayId);
- return;
- }
+ private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
PowerManager powerManager = mContext.getSystemService(PowerManager.class);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
TAG + ":" + displayId, displayId);
- mPerDisplayWakelocks.put(displayId, wakeLock);
wakeLock.acquire();
+ return wakeLock;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -872,8 +912,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
private void onSecureWindowShown(int displayId, int uid) {
- if (!mVirtualDisplayIds.contains(displayId)) {
- return;
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplays.contains(displayId)) {
+ return;
+ }
}
// If a virtual display isn't secure, the screen can't be captured. Show a warning toast
@@ -888,55 +930,102 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private ArraySet<UserHandle> getAllowedUserHandles() {
ArraySet<UserHandle> result = new ArraySet<>();
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- UserManager userManager = mContext.getSystemService(UserManager.class);
- for (UserHandle profile : userManager.getAllProfiles()) {
- int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(profile.getIdentifier());
- if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
- || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
- result.add(profile);
- } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
- if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ for (UserHandle profile : userManager.getAllProfiles()) {
+ int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(
+ profile.getIdentifier());
+ if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
+ || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
result.add(profile);
+ } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
+ if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+ result.add(profile);
+ }
}
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return result;
}
- void onVirtualDisplayRemovedLocked(int displayId) {
+
+ void onVirtualDisplayRemoved(int displayId) {
+ /* This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released
+ * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()).
+ * At this point, the display is already released, but we still need to release the
+ * corresponding wakeLock and unregister the RunningAppsChangedListener from corresponding
+ * WindowPolicyController.
+ *
+ * Note that when the display is destroyed during VirtualDeviceImpl.close() call,
+ * this callback won't be invoked because the display is removed from
+ * VirtualDeviceManagerService before any resources are released.
+ */
+ VirtualDisplayWrapper virtualDisplayWrapper;
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(displayId)) {
- throw new IllegalStateException(
- "Virtual device doesn't have a virtual display with ID " + displayId);
- }
- PowerManager.WakeLock wakeLock = mPerDisplayWakelocks.get(displayId);
- if (wakeLock != null) {
- wakeLock.release();
- mPerDisplayWakelocks.remove(displayId);
- }
- GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId);
- if (gwpc != null) {
- gwpc.unregisterRunningAppsChangedListener(/* listener= */ this);
- }
- mVirtualDisplayIds.remove(displayId);
- mWindowPolicyControllers.remove(displayId);
+ virtualDisplayWrapper = mVirtualDisplays.removeReturnOld(displayId);
+ }
+
+ if (virtualDisplayWrapper == null) {
+ throw new IllegalStateException(
+ "Virtual device doesn't have a virtual display with ID " + displayId);
}
+
+ releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+
+ }
+
+ /**
+ * Release resources tied to virtual display owned by this VirtualDevice instance.
+ *
+ * Note that this method won't release the virtual display itself.
+ *
+ * @param virtualDisplayWrapper - VirtualDisplayWrapper to release resources for.
+ */
+ private void releaseOwnedVirtualDisplayResources(VirtualDisplayWrapper virtualDisplayWrapper) {
+ virtualDisplayWrapper.getWakeLock().release();
+ virtualDisplayWrapper.getWindowPolicyController().unregisterRunningAppsChangedListener(
+ this);
}
int getOwnerUid() {
return mOwnerUid;
}
+ ArraySet<Integer> getDisplayIds() {
+ synchronized (mVirtualDeviceLock) {
+ final int size = mVirtualDisplays.size();
+ ArraySet<Integer> arraySet = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ arraySet.append(mVirtualDisplays.keyAt(i));
+ }
+ return arraySet;
+ }
+ }
+
+ @VisibleForTesting
+ GenericWindowPolicyController getDisplayWindowPolicyControllerForTest(int displayId) {
+ VirtualDisplayWrapper virtualDisplayWrapper;
+ synchronized (mVirtualDeviceLock) {
+ virtualDisplayWrapper = mVirtualDisplays.get(displayId);
+ }
+ return virtualDisplayWrapper != null ? virtualDisplayWrapper.getWindowPolicyController()
+ : null;
+ }
+
/**
* Returns true if an app with the given {@code uid} is currently running on this virtual
* device.
*/
boolean isAppRunningOnVirtualDevice(int uid) {
- final int size = mWindowPolicyControllers.size();
- for (int i = 0; i < size; i++) {
- if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
- return true;
+ synchronized (mVirtualDeviceLock) {
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
+ return true;
+ }
}
}
return false;
@@ -957,11 +1046,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Looper looper) {
synchronized (mVirtualDeviceLock) {
DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- final int size = mWindowPolicyControllers.size();
- for (int i = 0; i < size; i++) {
- if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
- int displayId = mWindowPolicyControllers.keyAt(i);
- Display display = displayManager.getDisplay(displayId);
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
+ Display display = displayManager.getDisplay(mVirtualDisplays.keyAt(i));
if (display != null && display.isValid()) {
Toast.makeText(mContext.createDisplayContext(display), looper, text,
duration).show();
@@ -972,7 +1059,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
boolean isDisplayOwnedByVirtualDevice(int displayId) {
- return mVirtualDisplayIds.contains(displayId);
+ synchronized (mVirtualDeviceLock) {
+ return mVirtualDisplays.contains(displayId);
+ }
}
void onEnteringPipBlocked(int uid) {
@@ -1016,10 +1105,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
- interface OnDeviceCloseListener {
- void onClose(int deviceId);
- }
-
interface PendingTrampolineCallback {
/**
* Called when the callback should start waiting for the given pending trampoline.
@@ -1073,4 +1158,31 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
+ ", displayId=" + mDisplayId + "}";
}
}
+
+ /** Data class wrapping resources tied to single virtual display. */
+ private static final class VirtualDisplayWrapper {
+ private final IVirtualDisplayCallback mToken;
+ private final GenericWindowPolicyController mWindowPolicyController;
+ private final PowerManager.WakeLock mWakeLock;
+
+ VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token,
+ @NonNull GenericWindowPolicyController windowPolicyController,
+ @NonNull PowerManager.WakeLock wakeLock) {
+ mToken = Objects.requireNonNull(token);
+ mWindowPolicyController = Objects.requireNonNull(windowPolicyController);
+ mWakeLock = Objects.requireNonNull(wakeLock);
+ }
+
+ GenericWindowPolicyController getWindowPolicyController() {
+ return mWindowPolicyController;
+ }
+
+ PowerManager.WakeLock getWakeLock() {
+ return mWakeLock;
+ }
+
+ IVirtualDisplayCallback getToken() {
+ return mToken;
+ }
+ }
}
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 9bb05a6bfe5d..3b1983f55fb6 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -37,7 +37,6 @@ import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.sensor.VirtualSensor;
import android.content.Context;
import android.content.Intent;
-import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.os.Binder;
@@ -85,7 +84,7 @@ public class VirtualDeviceManagerService extends SystemService {
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
private static AtomicInteger sNextUniqueIndex = new AtomicInteger(
- VirtualDeviceManager.DEVICE_ID_DEFAULT + 1);
+ Context.DEVICE_ID_DEFAULT + 1);
/**
* Mapping from device IDs to virtual devices.
@@ -108,26 +107,26 @@ public class VirtualDeviceManagerService extends SystemService {
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
- @Nullable
- @Override
- public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
- ActivityInterceptorInfo info) {
- if (info.getCallingPackage() == null) {
- return null;
- }
- PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
- if (pt == null) {
- return null;
- }
- pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
- ActivityOptions options = info.getCheckedOptions();
- if (options == null) {
- options = ActivityOptions.makeBasic();
- }
- return new ActivityInterceptResult(
- info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
- }
- };
+ @Nullable
+ @Override
+ public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
+ ActivityInterceptorInfo info) {
+ if (info.getCallingPackage() == null) {
+ return null;
+ }
+ PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
+ if (pt == null) {
+ return null;
+ }
+ pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
+ ActivityOptions options = info.getCheckedOptions();
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ return new ActivityInterceptResult(
+ info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
+ }
+ };
@Override
public void onStart() {
@@ -146,8 +145,8 @@ public class VirtualDeviceManagerService extends SystemService {
CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName();
mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid,
getContext().getString(
- com.android.internal.R.string.vdm_camera_access_denied,
- deviceName),
+ com.android.internal.R.string.vdm_camera_access_denied,
+ deviceName),
Toast.LENGTH_LONG, Looper.myLooper());
}
}
@@ -193,34 +192,46 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- @VisibleForTesting
void removeVirtualDevice(int deviceId) {
synchronized (mVirtualDeviceManagerLock) {
mAppsOnVirtualDevices.remove(deviceId);
mVirtualDevices.remove(deviceId);
}
+
+ Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED);
+ i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId);
+ i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ getContext().sendBroadcastAsUser(i, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
new VirtualDeviceImpl.PendingTrampolineCallback() {
- @Override
- public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
- PendingTrampoline existing = mPendingTrampolines.put(
- pendingTrampoline.mPendingIntent.getCreatorPackage(),
- pendingTrampoline);
- if (existing != null) {
- existing.mResultReceiver.send(
- VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null);
- }
- }
+ @Override
+ public void startWaitingForPendingTrampoline(
+ PendingTrampoline pendingTrampoline) {
+ PendingTrampoline existing = mPendingTrampolines.put(
+ pendingTrampoline.mPendingIntent.getCreatorPackage(),
+ pendingTrampoline);
+ if (existing != null) {
+ existing.mResultReceiver.send(
+ VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null);
+ }
+ }
- @Override
- public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
- mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage());
- }
- };
+ @Override
+ public void stopWaitingForPendingTrampoline(
+ PendingTrampoline pendingTrampoline) {
+ mPendingTrampolines.remove(
+ pendingTrampoline.mPendingIntent.getCreatorPackage());
+ }
+ };
@Override // Binder call
public IVirtualDevice createVirtualDevice(
@@ -251,8 +262,9 @@ public class VirtualDeviceManagerService extends SystemService {
final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
- associationInfo, token, callingUid, deviceId, cameraAccessController,
- this::onDeviceClosed, mPendingTrampolineCallback, activityListener,
+ associationInfo, VirtualDeviceManagerService.this, token, callingUid,
+ deviceId, cameraAccessController,
+ mPendingTrampolineCallback, activityListener,
soundEffectListener, runningAppsChangedCallback, params);
mVirtualDevices.put(deviceId, virtualDevice);
return virtualDevice;
@@ -281,26 +293,9 @@ public class VirtualDeviceManagerService extends SystemService {
"uid " + callingUid
+ " is not the owner of the supplied VirtualDevice");
}
- GenericWindowPolicyController gwpc;
- final long token = Binder.clearCallingIdentity();
- try {
- gwpc = virtualDeviceImpl.createWindowPolicyController(
- virtualDisplayConfig.getDisplayCategories());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- DisplayManagerInternal displayManager = getLocalService(
- DisplayManagerInternal.class);
- int displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
- virtualDevice, gwpc, packageName);
- final long tokenTwo = Binder.clearCallingIdentity();
- try {
- virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
- } finally {
- Binder.restoreCallingIdentity(tokenTwo);
- }
+ int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback,
+ packageName);
mLocalService.onVirtualDisplayCreated(displayId);
return displayId;
}
@@ -332,7 +327,7 @@ public class VirtualDeviceManagerService extends SystemService {
@Override // Binder call
public int getDeviceIdForDisplayId(int displayId) {
if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
- return VirtualDeviceManager.DEVICE_ID_DEFAULT;
+ return Context.DEVICE_ID_DEFAULT;
}
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mVirtualDevices.size(); i++) {
@@ -342,7 +337,7 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
}
- return VirtualDeviceManager.DEVICE_ID_DEFAULT;
+ return Context.DEVICE_ID_DEFAULT;
}
// Binder call
@@ -412,19 +407,6 @@ public class VirtualDeviceManagerService extends SystemService {
return null;
}
- private void onDeviceClosed(int deviceId) {
- removeVirtualDevice(deviceId);
- Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED);
- i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId);
- i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- final long identity = Binder.clearCallingIdentity();
- try {
- getContext().sendBroadcastAsUser(i, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -512,9 +494,14 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
final VirtualDisplayListener[] listeners;
+ VirtualDeviceImpl virtualDeviceImpl;
synchronized (mVirtualDeviceManagerLock) {
- ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+ virtualDeviceImpl = mVirtualDevices.get(
+ ((VirtualDeviceImpl) virtualDevice).getDeviceId());
+ }
+ if (virtualDeviceImpl != null) {
+ virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
}
mHandler.post(() -> {
for (VirtualDisplayListener listener : listeners) {
@@ -599,16 +586,11 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) {
+ VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
- int size = mVirtualDevices.size();
- for (int i = 0; i < size; i++) {
- VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
- if (device.getDeviceId() == deviceId) {
- return new ArraySet<>(device.mVirtualDisplayIds);
- }
- }
+ virtualDevice = mVirtualDevices.get(deviceId);
}
- return new ArraySet<>();
+ return virtualDevice == null ? new ArraySet<>() : virtualDevice.getDisplayIds();
}
@Override
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 92e322fa2fa6..e9a7f205c519 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -192,20 +192,21 @@ public final class BatteryService extends SystemService {
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
- private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true)
+ private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeferUntilActive(true)
.toBundle();
- private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
- new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true)
+ /** Used for both connected/disconnected, so match using key */
+ private Bundle mPowerOptions = BroadcastOptions.makeBasic()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeliveryGroupMatchingKey("android", Intent.ACTION_POWER_CONNECTED)
+ .setDeferUntilActive(true)
.toBundle();
- private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
- new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true)
- .toBundle();
- private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
- new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true)
- .toBundle();
- private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
- new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true)
+ /** Used for both low/okay, so match using key */
+ private Bundle mBatteryOptions = BroadcastOptions.makeBasic()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeliveryGroupMatchingKey("android", Intent.ACTION_BATTERY_OKAY)
+ .setDeferUntilActive(true)
.toBundle();
private MetricsLogger mMetricsLogger;
@@ -636,7 +637,7 @@ public final class BatteryService extends SystemService {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mPowerConnectedOptions);
+ mPowerOptions);
}
});
}
@@ -648,7 +649,7 @@ public final class BatteryService extends SystemService {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mPowerDisconnectedOptions);
+ mPowerOptions);
}
});
}
@@ -662,7 +663,7 @@ public final class BatteryService extends SystemService {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mBatteryLowOptions);
+ mBatteryOptions);
}
});
} else if (mSentLowBatteryBroadcast &&
@@ -675,7 +676,7 @@ public final class BatteryService extends SystemService {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mBatteryOkayOptions);
+ mBatteryOptions);
}
});
}
@@ -1210,6 +1211,11 @@ public final class BatteryService extends SystemService {
}
private final class Led {
+ // must match: config_notificationsBatteryLowBehavior in config.xml
+ static final int LOW_BATTERY_BEHAVIOR_DEFAULT = 0;
+ static final int LOW_BATTERY_BEHAVIOR_SOLID = 1;
+ static final int LOW_BATTERY_BEHAVIOR_FLASHING = 2;
+
private final LogicalLight mBatteryLight;
private final int mBatteryLowARGB;
@@ -1217,6 +1223,7 @@ public final class BatteryService extends SystemService {
private final int mBatteryFullARGB;
private final int mBatteryLedOn;
private final int mBatteryLedOff;
+ private final int mBatteryLowBehavior;
public Led(Context context, LightsManager lights) {
mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
@@ -1233,6 +1240,8 @@ public final class BatteryService extends SystemService {
com.android.internal.R.integer.config_notificationsBatteryLedOff);
mBatteryNearlyFullLevel = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryNearlyFullLevel);
+ mBatteryLowBehavior = context.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowBehavior);
}
/**
@@ -1245,13 +1254,26 @@ public final class BatteryService extends SystemService {
final int level = mHealthInfo.batteryLevel;
final int status = mHealthInfo.batteryStatus;
if (level < mLowBatteryWarningLevel) {
- if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- // Solid red when battery is charging
- mBatteryLight.setColor(mBatteryLowARGB);
- } else {
- // Flash red when battery is low and not charging
- mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED,
- mBatteryLedOn, mBatteryLedOff);
+ switch (mBatteryLowBehavior) {
+ case LOW_BATTERY_BEHAVIOR_SOLID:
+ // Solid red when low battery
+ mBatteryLight.setColor(mBatteryLowARGB);
+ break;
+ case LOW_BATTERY_BEHAVIOR_FLASHING:
+ // Flash red when battery is low and not charging
+ mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
+ break;
+ default:
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+ // Solid red when battery is charging
+ mBatteryLight.setColor(mBatteryLowARGB);
+ } else {
+ // Flash red when battery is low and not charging
+ mBatteryLight.setFlashing(mBatteryLowARGB,
+ LogicalLight.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff);
+ }
+ break;
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL) {
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 725ea5c1b3dd..19e5cb142cfd 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -79,6 +79,7 @@ import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.GZIPOutputStream;
@@ -105,6 +106,10 @@ public final class DropBoxManagerService extends SystemService {
// Size beyond which to force-compress newly added entries.
private static final long COMPRESS_THRESHOLD_BYTES = 16_384;
+ // Tags that we should drop by default.
+ private static final List<String> DISABLED_BY_DEFAULT_TAGS =
+ List.of("data_app_wtf", "system_app_wtf", "system_server_wtf");
+
// TODO: This implementation currently uses one file per entry, which is
// inefficient for smallish entries -- consider using a single queue file
// per tag (or even globally) instead.
@@ -549,8 +554,13 @@ public final class DropBoxManagerService extends SystemService {
public boolean isTagEnabled(String tag) {
final long token = Binder.clearCallingIdentity();
try {
- return !"disabled".equals(Settings.Global.getString(
+ if (DISABLED_BY_DEFAULT_TAGS.contains(tag)) {
+ return "enabled".equals(Settings.Global.getString(
+ mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag));
+ } else {
+ return !"disabled".equals(Settings.Global.getString(
mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag));
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 3f1ad3ae0587..2a46d862b991 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,18 +16,12 @@
package com.android.server;
-import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS;
-
-import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
-import android.os.Binder;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.FastImmutableArraySet;
@@ -40,7 +34,6 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FastPrintWriter;
-import com.android.server.am.ActivityManagerUtils;
import com.android.server.pm.Computer;
import com.android.server.pm.snapshot.PackageDataSnapshot;
@@ -88,7 +81,7 @@ public abstract class IntentResolver<F, R extends Object> {
* Returns whether an intent matches the IntentFilter with a pre-resolved type.
*/
public static boolean intentMatchesFilter(
- IntentFilter filter, Intent intent, String resolvedType, boolean blockNullAction) {
+ IntentFilter filter, Intent intent, String resolvedType) {
final boolean debug = localLOGV
|| ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
@@ -102,8 +95,7 @@ public abstract class IntentResolver<F, R extends Object> {
}
final int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(),
- intent.getData(), intent.getCategories(), TAG, /* supportWildcards */ false,
- blockNullAction, null, null);
+ intent.getData(), intent.getCategories(), TAG);
if (match >= 0) {
if (debug) {
@@ -358,32 +350,14 @@ public abstract class IntentResolver<F, R extends Object> {
return Collections.unmodifiableSet(mFilters);
}
- private boolean blockNullAction(Computer computer, Intent intent,
- String resolvedType, int callingUid, boolean debug) {
- if (intent.getAction() == null) {
- final boolean blockNullAction = UserHandle.isCore(callingUid)
- || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, callingUid);
- ActivityManagerUtils.logUnsafeIntentEvent(
- UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH,
- callingUid, intent, resolvedType, blockNullAction);
- if (blockNullAction) {
- if (debug) Slog.v(TAG, "Skip matching filters: action is null");
- return true;
- }
- }
- return false;
- }
-
public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
- String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut,
- int callingUid, @UserIdInt int userId, long customFlags) {
+ String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
+ long customFlags) {
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
- if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return resultList;
-
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
final String scheme = intent.getScheme();
int N = listCut.size();
@@ -391,26 +365,18 @@ public abstract class IntentResolver<F, R extends Object> {
buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme,
listCut.get(i), resultList, userId, customFlags);
}
- filterResults(computer, intent, resultList);
+ filterResults(resultList);
sortResults(resultList);
return resultList;
}
- public final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
- String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
- return queryIntent(snapshot, intent, resolvedType, defaultOnly,
- Binder.getCallingUid(), userId, 0);
- }
-
public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
- String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) {
- return queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid, userId, 0);
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0);
}
protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
- String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId,
- long customFlags) {
- final Computer computer = (Computer) snapshot;
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
@@ -422,8 +388,6 @@ public abstract class IntentResolver<F, R extends Object> {
TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
+ " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
- if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return finalList;
-
F[] firstTypeCut = null;
F[] secondTypeCut = null;
F[] thirdTypeCut = null;
@@ -484,6 +448,7 @@ public abstract class IntentResolver<F, R extends Object> {
}
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+ Computer computer = (Computer) snapshot;
if (firstTypeCut != null) {
buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
scheme, firstTypeCut, finalList, userId, customFlags);
@@ -500,7 +465,7 @@ public abstract class IntentResolver<F, R extends Object> {
buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
scheme, schemeCut, finalList, userId, customFlags);
}
- filterResults(computer, intent, finalList);
+ filterResults(finalList);
sortResults(finalList);
if (debug) {
@@ -569,8 +534,7 @@ public abstract class IntentResolver<F, R extends Object> {
/**
* Apply filtering to the results. This happens before the results are sorted.
*/
- protected void filterResults(@NonNull Computer computer, @NonNull Intent intent,
- List<R> results) {
+ protected void filterResults(List<R> results) {
}
protected void dumpFilter(PrintWriter out, String prefix, F filter) {
@@ -802,11 +766,7 @@ public abstract class IntentResolver<F, R extends Object> {
continue;
}
- match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG,
- false /*supportWildcards*/,
- false /*blockNullAction: already handled*/,
- null /*ignoreActions*/,
- null /*extras*/);
+ match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG);
if (match >= 0) {
if (debug) Slog.v(TAG, " Filter matched! match=0x" +
Integer.toHexString(match) + " hasDefault="
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c16314b6a117..225afea3d1b7 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -38,6 +38,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -195,6 +196,9 @@ public class AccountManagerService
private final IAccountAuthenticatorCache mAuthenticatorCache;
private static final String PRE_N_DATABASE_NAME = "accounts.db";
private static final Intent ACCOUNTS_CHANGED_INTENT;
+ private static final Bundle ACCOUNTS_CHANGED_OPTIONS = new BroadcastOptions()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .toBundle();
private static final int SIGNATURE_CHECK_MISMATCH = 0;
private static final int SIGNATURE_CHECK_MATCH = 1;
@@ -1075,7 +1079,8 @@ public class AccountManagerService
Log.i(TAG, "the accountType= " + (accountType == null ? "" : accountType)
+ " changed with useCase=" + useCase + " for userId=" + userId
+ ", sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction());
- mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
+ mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId),
+ null /* receiverPermission */, ACCOUNTS_CHANGED_OPTIONS);
}
private void sendAccountRemovedBroadcast(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 70304c55067e..c1850bda609b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -7822,7 +7822,7 @@ public final class ActiveServices {
final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null
? r.mRecentCallerApplicationInfo.targetSdkVersion : 0;
- // TODO(short-service): Log BFSL too.
+ // TODO(short-service): Log the UID capabilities (for BFSL) too, and also the procstate?
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid,
r.shortInstanceName,
@@ -7872,7 +7872,8 @@ public final class ActiveServices {
r.mFgsNotificationShown ? 1 : 0,
durationMs,
r.mStartForegroundCount,
- fgsStopReasonToString(fgsStopReason));
+ fgsStopReasonToString(fgsStopReason),
+ r.foregroundServiceType);
}
private void updateNumForegroundServicesLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 74f799060b46..a8054deff8db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1128,19 +1128,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- protected void filterResults(@NonNull Computer computer,
- @NonNull Intent intent, List<BroadcastFilter> results) {
- if (intent.getAction() != null) return;
- // When the resolved component is targeting U+, block null action intents
- for (int i = results.size() - 1; i >= 0; --i) {
- if (computer.isChangeEnabled(
- IntentFilter.BLOCK_NULL_ACTION_INTENTS, results.get(i).owningUid)) {
- results.remove(i);
- }
- }
- }
-
- @Override
protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
return input;
}
@@ -3537,7 +3524,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// We'll take the stack crawls of just the top apps using CPU.
final int workingStatsNumber = processCpuTracker.countWorkingStats();
- for (int i = 0; i < workingStatsNumber && extraPids.size() < 5; i++) {
+ for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
if (lastPids.indexOfKey(stats.pid) >= 0) {
if (DEBUG_ANR) {
@@ -7015,36 +7002,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either, it needs cross-user uri grants.
- *
- * CTS tests for this functionality can be run with "runtest cts-appsecurity".
- *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
- * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
- *
- * @deprecated -- use getProviderMimeTypeAsync.
- */
- @Deprecated
- @Override
- public String getProviderMimeType(Uri uri, int userId) {
- return mCpHelper.getProviderMimeType(uri, userId);
- }
-
- /**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either way, it needs cross-user uri grants.
- */
- @Override
- public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
- mCpHelper.getProviderMimeTypeAsync(uri, userId, resultCallback);
- }
-
- /**
* Filters calls to getType based on permission. If the caller has required permission,
* then it returns the contentProvider#getType.
* Else, it returns the contentProvider#getTypeAnonymous, which does not
@@ -13978,19 +13935,11 @@ public class ActivityManagerService extends IActivityManager.Stub
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
continue;
}
-
- final boolean blockNullAction = mPlatformCompat.isChangeEnabledInternal(
- IntentFilter.BLOCK_NULL_ACTION_INTENTS, callerApp.info);
// If intent has scheme "content", it will need to access
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
- if (filter.match(intent.getAction(), intent.resolveType(resolver),
- intent.getScheme(), intent.getData(), intent.getCategories(), TAG,
- false /* supportWildcards */,
- blockNullAction,
- null /* ignoreActions */,
- intent.getExtras()) >= 0) {
+ if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
@@ -15009,7 +14958,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(snapshot, intent,
- resolvedType, false /*defaultOnly*/, callingUid, users[i]);
+ resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
@@ -15018,7 +14967,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
- resolvedType, false /*defaultOnly*/, callingUid, userId);
+ resolvedType, false /*defaultOnly*/, userId);
}
}
BroadcastQueue.traceEnd(cookie);
@@ -16353,6 +16302,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
// the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
+ @Nullable
ProcessRecord getTopApp() {
final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
@@ -18208,8 +18158,9 @@ public class ActivityManagerService extends IActivityManager.Stub
bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
PowerExemptionManager.REASON_LOCALE_CHANGED, "");
- bOptions.setRemoveMatchingFilter(
- new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+ bOptions.setDeliveryGroupPolicy(
+ BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
+ bOptions.setDeferUntilActive(true);
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index aa9d4ccc61ab..4c1835eb80f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -146,6 +146,7 @@ import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -595,10 +596,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 1;
}
- String mimeType = intent.getType();
- if (mimeType == null && intent.getData() != null
+ AtomicReference<String> mimeType = new AtomicReference<>(intent.getType());
+
+ if (mimeType.get() == null && intent.getData() != null
&& "content".equals(intent.getData().getScheme())) {
- mimeType = mInterface.getProviderMimeType(intent.getData(), mUserId);
+ mInterface.getMimeTypeFilterAsync(intent.getData(), mUserId,
+ new RemoteCallback(result -> {
+ mimeType.set(result.getPairValue());
+ }));
}
do {
@@ -611,8 +616,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
int userIdForQuery = mInternal.mUserController.handleIncomingUser(
Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false,
ALLOW_NON_FULL, "ActivityManagerShellCommand", null);
- List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
- userIdForQuery).getList();
+ List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType.get(),
+ 0, userIdForQuery).getList();
if (activities == null || activities.size() <= 0) {
getErrPrintWriter().println("Error: Intent does not match any activities: "
+ intent);
@@ -708,12 +713,12 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
if (mWaitOption) {
result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
- mimeType, null, null, 0, mStartFlags, profilerInfo,
+ mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
- intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
+ intent, mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();
@@ -3531,9 +3536,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
if (foregroundActivities) {
try {
int prcState = mIam.getUidProcessState(uid, "android");
- int topPid = mInternal.getTopApp().getPid();
- if (prcState == ProcessStateEnum.TOP && topPid == pid) {
- mPw.println("New foreground process: " + pid);
+ ProcessRecord topApp = mInternal.getTopApp();
+ if (topApp == null) {
+ mPw.println("No top app found");
+ } else {
+ int topPid = topApp.getPid();
+ if (prcState == ProcessStateEnum.TOP && topPid == pid) {
+ mPw.println("New foreground process: " + pid);
+ }
}
mPw.flush();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java
index 01466b845a61..9be553c49a35 100644
--- a/services/core/java/com/android/server/am/ActivityManagerUtils.java
+++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java
@@ -17,13 +17,11 @@ package com.android.server.am;
import android.app.ActivityThread;
import android.content.ContentResolver;
-import android.content.Intent;
import android.provider.Settings;
import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -135,25 +133,4 @@ public class ActivityManagerUtils {
public static int hashComponentNameForAtom(String shortInstanceName) {
return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash();
}
-
- /**
- * Helper method to log an unsafe intent event.
- */
- public static void logUnsafeIntentEvent(int event, int callingUid,
- Intent intent, String resolvedType, boolean blocked) {
- String[] categories = intent.getCategories() == null ? new String[0]
- : intent.getCategories().toArray(String[]::new);
- String component = intent.getComponent() == null ? null
- : intent.getComponent().flattenToString();
- FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED,
- event,
- callingUid,
- component,
- intent.getPackage(),
- intent.getAction(),
- categories,
- resolvedType,
- intent.getScheme(),
- blocked);
- }
}
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 463a2f84aa6b..16219cd5b2ea 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -49,7 +49,7 @@ class AnrHelper {
* this time, the information might be outdated. So we only the dump the unresponsive process
* instead of including other processes to avoid making the system more busy.
*/
- private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.MINUTES.toMillis(1);
+ private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.SECONDS.toMillis(10);
/**
* If the last ANR occurred within this given time, consider it's anomaly.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b942f4b96b21..841b61e8e81f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -597,18 +597,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
final int cookie = traceBegin("enqueueBroadcast");
r.applySingletonPolicy(mService);
- final IntentFilter removeMatchingFilter = (r.options != null)
- ? r.options.getRemoveMatchingFilter() : null;
- if (removeMatchingFilter != null) {
- final Predicate<Intent> removeMatching = removeMatchingFilter.asPredicate();
- forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> {
- // We only allow caller to remove broadcasts they enqueued
- return (r.callingUid == testRecord.callingUid)
- && (r.userId == testRecord.userId)
- && removeMatching.test(testRecord.intent);
- }, mBroadcastConsumerSkipAndCanceled, true);
- }
-
applyDeliveryGroupPolicy(r);
r.enqueueTime = SystemClock.uptimeMillis();
@@ -909,6 +897,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
final IApplicationThread thread = app.getOnewayThread();
if (thread != null) {
try {
+ if (r.shareIdentity) {
+ mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
+ UserHandle.getAppId(app.uid), r.callingUid, true);
+ }
if (receiver instanceof BroadcastFilter) {
notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
thread.scheduleRegisteredReceiver(
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index f721d69958f5..48df1494fbe8 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -969,122 +969,6 @@ public class ContentProviderHelper {
}
/**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either, it needs cross-user uri grants.
- *
- * CTS tests for this functionality can be run with "runtest cts-appsecurity".
- *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
- * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
- *
- * @deprecated -- use getProviderMimeTypeAsync.
- */
- @Deprecated
- String getProviderMimeType(Uri uri, int userId) {
- mService.enforceNotIsolatedCaller("getProviderMimeType");
- final String name = uri.getAuthority();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
- ? Binder.clearCallingIdentity() : 0;
- final ContentProviderHolder holder;
- try {
- holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid,
- "*getmimetype*", safeUserId);
- } finally {
- if (ident != 0) {
- Binder.restoreCallingIdentity(ident);
- }
- }
- try {
- if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
- final IBinder providerConnection = holder.connection;
- final ComponentName providerName = holder.info.getComponentName();
- // Note: creating a new Runnable instead of using a lambda here since lambdas in
- // java provide no guarantee that there will be a new instance returned every call.
- // Hence, it's possible that a cached copy is returned and the ANR is executed on
- // the incorrect provider.
- final Runnable providerNotResponding = new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
- appNotRespondingViaProvider(providerConnection);
- }
- };
- mService.mHandler.postDelayed(providerNotResponding, 1000);
- try {
- final String type = holder.provider.getType(uri);
- return type;
- } finally {
- mService.mHandler.removeCallbacks(providerNotResponding);
- // We need to clear the identity to call removeContentProviderExternalUnchecked
- final long token = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, null /* token */, safeUserId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- return null;
- } catch (Exception e) {
- Log.w(TAG, "Exception while determining type of " + uri, e);
- return null;
- }
-
- return null;
- }
-
- /**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either way, it needs cross-user uri grants.
- */
- void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
- mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync");
- final String name = uri.getAuthority();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
- ? Binder.clearCallingIdentity() : 0;
- final ContentProviderHolder holder;
- try {
- holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid,
- "*getmimetype*", safeUserId);
- } finally {
- if (ident != 0) {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- try {
- if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
- holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
- final long identity = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, null, safeUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- resultCallback.sendResult(result);
- }));
- } else {
- resultCallback.sendResult(Bundle.EMPTY);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- resultCallback.sendResult(Bundle.EMPTY);
- }
- }
-
- /**
* Filters calls to getType based on permission. If the caller has required permission,
* then it returns the contentProvider#getType.
* Else, it returns the contentProvider#getTypeAnonymous, which does not
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 1534ff5651de..50841ae4488c 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -122,9 +122,9 @@ option java_package com.android.server.am
30091 um_user_visibility_changed (userId|1|5),(visible|1)
# Foreground service start/stop events.
-30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
-30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
-30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
+30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
+30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
+30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0c366268604d..d05301a21a18 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -207,7 +207,8 @@ public class OomAdjuster {
return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN;
case OOM_ADJ_REASON_PROCESS_END:
return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END;
- case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: // TODO(short-service) add value to AppProtoEnums
+ case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT:
+ return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 88c0c7ff052b..0d0e5764b522 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1315,7 +1315,7 @@ public class AudioService extends IAudioService.Stub
// persistent data
initVolumeGroupStates();
- mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
+ mSoundDoseHelper.initSafeMediaVolumeIndex();
// Link VGS on VSS
initVolumeStreamStates();
@@ -8358,6 +8358,7 @@ public class AudioService extends IAudioService.Stub
synchronized (VolumeStreamState.class) {
// apply device specific volumes first
int index;
+ boolean isAbsoluteVolume = false;
for (int i = 0; i < mIndexMap.size(); i++) {
final int device = mIndexMap.keyAt(i);
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
@@ -8366,6 +8367,7 @@ public class AudioService extends IAudioService.Stub
} else if (isAbsoluteVolumeDevice(device)
|| isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device)) {
+ isAbsoluteVolume = true;
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -8374,6 +8376,11 @@ public class AudioService extends IAudioService.Stub
} else {
index = (mIndexMap.valueAt(i) + 5)/10;
}
+
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
+ SENDMSG_REPLACE, device, isAbsoluteVolume ? 1 : 0, this,
+ /*delay=*/0);
+
setStreamVolumeIndex(index, device);
}
}
@@ -8830,7 +8837,7 @@ public class AudioService extends IAudioService.Stub
final VolumeStreamState streamState = mStreamStates[update.mStreamType];
if (update.hasVolumeIndex()) {
int index = update.getVolumeIndex();
- if (!mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
+ if (mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
index = mSoundDoseHelper.safeMediaVolumeIndex(update.mDevice);
}
streamState.setIndex(index, update.mDevice, update.mCaller,
@@ -8848,6 +8855,10 @@ public class AudioService extends IAudioService.Stub
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_REPLACE,
+ device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
+ streamState, /*delay=*/0);
// Apply volume
streamState.applyDeviceVolume_syncVSS(device);
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 4e8e70420955..cf81dbe08182 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -52,10 +52,10 @@ import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@@ -79,7 +79,6 @@ public class SoundDoseHelper {
// SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
// can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
// (when user opts out).
- // Note: when CSD calculation is enabled the state is set to SAFE_MEDIA_VOLUME_DISABLED
private static final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
private static final int SAFE_MEDIA_VOLUME_DISABLED = 1;
private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed
@@ -89,9 +88,12 @@ public class SoundDoseHelper {
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 2;
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 3;
private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 4;
+ /*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 5;
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
+ private static final int MOMENTARY_EXPOSURE_TIMEOUT_MS = (20 * 3600 * 1000); // 20 hours
+
// 30s after boot completed
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000;
@@ -125,23 +127,50 @@ public class SoundDoseHelper {
// mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
private int mSafeMediaVolumeIndex;
- // mSafeUsbMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB
+ // mSafeMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB
// property, divided by 100.0.
- private float mSafeUsbMediaVolumeDbfs;
-
- // mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index
- // corresponding to a gain of mSafeUsbMediaVolumeDbfs (defaulting to -37dB) in audio
- // flinger mixer.
- // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
- // amplification when both effects are on with all band gains at maximum.
- // This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
- // the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
- private int mSafeUsbMediaVolumeIndex;
- // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
- private final Set<Integer> mSafeMediaVolumeDevices = new HashSet<>(
- Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET));
+ // For now using the same value for CSD supported devices
+ private float mSafeMediaVolumeDbfs;
+
+ private static class SafeDeviceVolumeInfo {
+ int mDeviceType;
+ int mSafeVolumeIndex = -1;
+
+ SafeDeviceVolumeInfo(int deviceType) {
+ mDeviceType = deviceType;
+ }
+ }
+ /**
+ * mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced.
+ * Contains a safe volume index for a given device type.
+ * Indexes are used for headsets and is the music volume UI index
+ * corresponding to a gain of mSafeMediaVolumeDbfs (defaulting to -37dB) in audio
+ * flinger mixer.
+ * We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
+ * amplification when both effects are on with all band gains at maximum.
+ * This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
+ * the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
+ */
+ private final HashMap<Integer, SafeDeviceVolumeInfo> mSafeMediaVolumeDevices =
+ new HashMap<>() {{
+ put(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADSET));
+ put(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE));
+ put(AudioSystem.DEVICE_OUT_USB_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_USB_HEADSET));
+ put(AudioSystem.DEVICE_OUT_BLE_HEADSET,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_HEADSET));
+ put(AudioSystem.DEVICE_OUT_BLE_BROADCAST,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_BROADCAST));
+ put(AudioSystem.DEVICE_OUT_HEARING_AID,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_HEARING_AID));
+ put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES));
+ put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ }};
// mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
// When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
@@ -158,12 +187,16 @@ public class SoundDoseHelper {
private final boolean mEnableCsd;
- private ISoundDose mSoundDose;
-
private final Object mCsdStateLock = new Object();
+ private final AtomicReference<ISoundDose> mSoundDose = new AtomicReference<>();
+
@GuardedBy("mCsdStateLock")
private float mCurrentCsd = 0.f;
+
+ @GuardedBy("mCsdStateLock")
+ private long mLastMomentaryExposureTimeMs = -1;
+
// dose at which the next dose reached warning occurs
@GuardedBy("mCsdStateLock")
private float mNextCsdWarning = 1.0f;
@@ -179,10 +212,26 @@ public class SoundDoseHelper {
private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() {
public void onMomentaryExposure(float currentMel, int deviceId) {
+ if (!mEnableCsd) {
+ Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback");
+ return;
+ }
+
Log.w(TAG, "DeviceId " + deviceId + " triggered momentary exposure with value: "
+ currentMel);
mLogger.enqueue(SoundDoseEvent.getMomentaryExposureEvent(currentMel));
- if (mEnableCsd) {
+
+ boolean postWarning = false;
+ synchronized (mCsdStateLock) {
+ if (mLastMomentaryExposureTimeMs < 0
+ || (System.currentTimeMillis() - mLastMomentaryExposureTimeMs)
+ >= MOMENTARY_EXPOSURE_TIMEOUT_MS) {
+ mLastMomentaryExposureTimeMs = System.currentTimeMillis();
+ postWarning = true;
+ }
+ }
+
+ if (postWarning) {
mVolumeController.postDisplayCsdWarning(
AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE,
getTimeoutMsForWarning(AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE));
@@ -241,12 +290,10 @@ public class SoundDoseHelper {
mContext = context;
mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default);
- if (mEnableCsd) {
- mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
- } else {
- mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
- Settings.Global.AUDIO_SAFE_VOLUME_STATE, 0);
- }
+ initCsd();
+
+ mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
+ Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
// The default safe volume index read here will be replaced by the actual value when
// the mcc is read by onConfigureSafeMedia()
@@ -263,9 +310,14 @@ public class SoundDoseHelper {
return 0.f;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return 0.f;
+ }
+
try {
- return mSoundDose.getOutputRs2();
+ return soundDose.getOutputRs2();
} catch (RemoteException e) {
Log.e(TAG, "Exception while getting the RS2 exposure value", e);
return 0.f;
@@ -277,9 +329,14 @@ public class SoundDoseHelper {
return;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return;
+ }
+
try {
- mSoundDose.setOutputRs2(rs2Value);
+ soundDose.setOutputRs2(rs2Value);
} catch (RemoteException e) {
Log.e(TAG, "Exception while setting the RS2 exposure value", e);
}
@@ -290,9 +347,14 @@ public class SoundDoseHelper {
return -1.f;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return -1.f;
+ }
+
try {
- return mSoundDose.getCsd();
+ return soundDose.getCsd();
} catch (RemoteException e) {
Log.e(TAG, "Exception while getting the CSD value", e);
return -1.f;
@@ -304,13 +366,18 @@ public class SoundDoseHelper {
return;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return;
+ }
+
try {
final SoundDoseRecord record = new SoundDoseRecord();
record.timestamp = System.currentTimeMillis();
record.value = csd;
final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record };
- mSoundDose.resetCsd(csd, recordArray);
+ soundDose.resetCsd(csd, recordArray);
} catch (RemoteException e) {
Log.e(TAG, "Exception while setting the CSD value", e);
}
@@ -321,9 +388,14 @@ public class SoundDoseHelper {
return;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return;
+ }
+
try {
- mSoundDose.forceUseFrameworkMel(useFrameworkMel);
+ soundDose.forceUseFrameworkMel(useFrameworkMel);
} catch (RemoteException e) {
Log.e(TAG, "Exception while forcing the internal MEL computation", e);
}
@@ -334,9 +406,14 @@ public class SoundDoseHelper {
return;
}
- Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Sound dose interface not initialized");
+ return;
+ }
+
try {
- mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+ soundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
} catch (RemoteException e) {
Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
}
@@ -347,14 +424,12 @@ public class SoundDoseHelper {
}
/*package*/ int safeMediaVolumeIndex(int device) {
- if (!mSafeMediaVolumeDevices.contains(device)) {
+ final SafeDeviceVolumeInfo vi = mSafeMediaVolumeDevices.get(device);
+ if (vi == null) {
return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
}
- if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
- return mSafeUsbMediaVolumeIndex;
- } else {
- return mSafeMediaVolumeIndex;
- }
+
+ return vi.mSafeVolumeIndex;
}
/*package*/ void restoreMusicActiveMs() {
@@ -378,20 +453,24 @@ public class SoundDoseHelper {
/*package*/ void enforceSafeMediaVolume(String caller) {
AudioService.VolumeStreamState streamState = mAudioService.getVssVolumeForStream(
AudioSystem.STREAM_MUSIC);
- Set<Integer> devices = mSafeMediaVolumeDevices;
- for (int device : devices) {
- int index = streamState.getIndex(device);
- int safeIndex = safeMediaVolumeIndex(device);
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ int index = streamState.getIndex(vi.mDeviceType);
+ int safeIndex = safeMediaVolumeIndex(vi.mDeviceType);
if (index > safeIndex) {
- streamState.setIndex(safeIndex, device, caller, true /*hasModifyAudioSettings*/);
+ streamState.setIndex(safeIndex, vi.mDeviceType, caller,
+ true /*hasModifyAudioSettings*/);
mAudioHandler.sendMessageAtTime(
- mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, device, /*arg2=*/0,
- streamState), /*delay=*/0);
+ mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, vi.mDeviceType,
+ /*arg2=*/0, streamState), /*delay=*/0);
}
}
}
+ /**
+ * Returns {@code true} if the safe media actions can be applied for the given stream type,
+ * volume index and device.
+ **/
/*package*/ boolean checkSafeMediaVolume(int streamType, int index, int device) {
boolean result;
synchronized (mSafeMediaVolumeStateLock) {
@@ -402,17 +481,16 @@ public class SoundDoseHelper {
@GuardedBy("mSafeMediaVolumeStateLock")
private boolean checkSafeMediaVolume_l(int streamType, int index, int device) {
- return (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_ACTIVE)
- || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC)
- || (!mSafeMediaVolumeDevices.contains(device))
- || (index <= safeMediaVolumeIndex(device))
- || mEnableCsd;
+ return (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)
+ && (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC)
+ && (mSafeMediaVolumeDevices.containsKey(device))
+ && (index > safeMediaVolumeIndex(device));
}
/*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device,
int flags) {
synchronized (mSafeMediaVolumeStateLock) {
- if (!checkSafeMediaVolume_l(streamType, index, device)) {
+ if (checkSafeMediaVolume_l(streamType, index, device)) {
mVolumeController.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
@@ -443,15 +521,13 @@ public class SoundDoseHelper {
/*package*/ void scheduleMusicActiveCheck() {
synchronized (mSafeMediaVolumeStateLock) {
cancelMusicActiveCheck();
- if (!mEnableCsd) {
- mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_CHECK_MUSIC_ACTIVE,
- new Intent(ACTION_CHECK_MUSIC_ACTIVE),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime()
- + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent);
- }
+ mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_CHECK_MUSIC_ACTIVE,
+ new Intent(ACTION_CHECK_MUSIC_ACTIVE),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime()
+ + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent);
}
}
@@ -459,7 +535,7 @@ public class SoundDoseHelper {
synchronized (mSafeMediaVolumeStateLock) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC);
- if (mSafeMediaVolumeDevices.contains(device) && isStreamActive) {
+ if (mSafeMediaVolumeDevices.containsKey(device) && isStreamActive) {
scheduleMusicActiveCheck();
int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
device);
@@ -487,27 +563,31 @@ public class SoundDoseHelper {
/*package*/ void configureSafeMedia(boolean forced, String caller) {
int msg = MSG_CONFIGURE_SAFE_MEDIA;
- mAudioHandler.removeMessages(msg);
+ if (forced) {
+ // unforced should not cancel forced configure messages
+ mAudioHandler.removeMessages(msg);
+ }
long time = 0;
if (forced) {
time = (SystemClock.uptimeMillis() + (SystemProperties.getBoolean(
"audio.safemedia.bypass", false) ? 0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS));
}
+
mAudioHandler.sendMessageAtTime(
mAudioHandler.obtainMessage(msg, /*arg1=*/forced ? 1 : 0, /*arg2=*/0, caller),
time);
}
- /*package*/ void initSafeUsbMediaVolumeIndex() {
- // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
- // relies on audio policy having correct ranges for volume indexes.
- mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ /*package*/ void initSafeMediaVolumeIndex() {
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ vi.mSafeVolumeIndex = getSafeDeviceMediaVolumeIndex(vi.mDeviceType);
+ }
}
/*package*/ int getSafeMediaVolumeIndex(int device) {
- if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains(
- device)) {
+ if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE
+ && mSafeMediaVolumeDevices.containsKey(device)) {
return safeMediaVolumeIndex(device);
} else {
return -1;
@@ -516,7 +596,7 @@ public class SoundDoseHelper {
/*package*/ boolean raiseVolumeDisplaySafeMediaVolume(int streamType, int index, int device,
int flags) {
- if (checkSafeMediaVolume(streamType, index, device)) {
+ if (!checkSafeMediaVolume(streamType, index, device)) {
return false;
}
@@ -525,7 +605,7 @@ public class SoundDoseHelper {
}
/*package*/ boolean safeDevicesContains(int device) {
- return mSafeMediaVolumeDevices.contains(device);
+ return mSafeMediaVolumeDevices.containsKey(device);
}
/*package*/ void invalidatPendingVolumeCommand() {
@@ -551,6 +631,15 @@ public class SoundDoseHelper {
case MSG_PERSIST_CSD_VALUES:
onPersistSoundDoseRecords();
break;
+ case MSG_CSD_UPDATE_ATTENUATION:
+ final int device = msg.arg1;
+ final boolean isAbsoluteVolume = (msg.arg2 == 1);
+ final AudioService.VolumeStreamState streamState =
+ (AudioService.VolumeStreamState) msg.obj;
+
+ updateDoseAttenuation(streamState.getIndex(device), device,
+ streamState.getStreamType(), isAbsoluteVolume);
+ break;
default:
Log.e(TAG, "Unexpected msg to handle: " + msg.what);
break;
@@ -562,8 +651,11 @@ public class SoundDoseHelper {
pw.print(" mSafeMediaVolumeState=");
pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState));
pw.print(" mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex);
- pw.print(" mSafeUsbMediaVolumeIndex="); pw.println(mSafeUsbMediaVolumeIndex);
- pw.print(" mSafeUsbMediaVolumeDbfs="); pw.println(mSafeUsbMediaVolumeDbfs);
+ for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) {
+ pw.print(" mSafeMediaVolumeIndex["); pw.print(vi.mDeviceType);
+ pw.print("]="); pw.println(vi.mSafeVolumeIndex);
+ }
+ pw.print(" mSafeMediaVolumeDbfs="); pw.println(mSafeMediaVolumeDbfs);
pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs);
pw.print(" mMcc="); pw.println(mMcc);
pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand);
@@ -574,16 +666,18 @@ public class SoundDoseHelper {
/*package*/void reset() {
Log.d(TAG, "Reset the sound dose helper");
- mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
+ mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+
synchronized (mCsdStateLock) {
try {
- if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) {
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose != null && soundDose.asBinder().isBinderAlive()) {
if (mCurrentCsd != 0.f) {
Log.d(TAG,
"Resetting the saved sound dose value " + mCurrentCsd);
SoundDoseRecord[] records = mDoseRecords.toArray(
new SoundDoseRecord[0]);
- mSoundDose.resetCsd(mCurrentCsd, records);
+ soundDose.resetCsd(mCurrentCsd, records);
}
}
} catch (RemoteException e) {
@@ -592,34 +686,69 @@ public class SoundDoseHelper {
}
}
+ private void updateDoseAttenuation(int newIndex, int device, int streamType,
+ boolean isAbsoluteVolume) {
+ if (!mEnableCsd) {
+ return;
+ }
+
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Can not apply attenuation. ISoundDose itf is null.");
+ return;
+ }
+
+ try {
+ if (!isAbsoluteVolume) {
+ // remove any possible previous attenuation
+ soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
+
+ return;
+ }
+
+ if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
+ && mSafeMediaVolumeDevices.containsKey(device)) {
+ soundDose.updateAttenuation(
+ AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
+ (newIndex + 5) / 10,
+ device), device);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not apply the attenuation for MEL calculation with volume index "
+ + newIndex, e);
+ }
+ }
+
private void initCsd() {
- if (mEnableCsd) {
- Log.v(TAG, "Initializing sound dose");
+ if (!mEnableCsd) {
+ return;
+ }
- synchronized (mCsdStateLock) {
- if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
- mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
- }
+ Log.v(TAG, "Initializing sound dose");
- float prevCsd = mCurrentCsd;
- // Restore persisted values
- mCurrentCsd = parseGlobalSettingFloat(
- Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
- if (mCurrentCsd != prevCsd) {
- mNextCsdWarning = parseGlobalSettingFloat(
- Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
- final List<SoundDoseRecord> records = persistedStringToRecordList(
- mSettings.getGlobalString(mAudioService.getContentResolver(),
- Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
- mGlobalTimeOffsetInSecs);
- if (records != null) {
- mDoseRecords.addAll(records);
- }
- }
+ synchronized (mCsdStateLock) {
+ if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+ mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
}
- reset();
+ float prevCsd = mCurrentCsd;
+ // Restore persisted values
+ mCurrentCsd = parseGlobalSettingFloat(
+ Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f);
+ if (mCurrentCsd != prevCsd) {
+ mNextCsdWarning = parseGlobalSettingFloat(
+ Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f);
+ final List<SoundDoseRecord> records = persistedStringToRecordList(
+ mSettings.getGlobalString(mAudioService.getContentResolver(),
+ Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS),
+ mGlobalTimeOffsetInSecs);
+ if (records != null) {
+ mDoseRecords.addAll(records);
+ }
+ }
}
+
+ reset();
}
private void onConfigureSafeMedia(boolean force, String caller) {
@@ -629,7 +758,7 @@ public class SoundDoseHelper {
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
- mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ initSafeMediaVolumeIndex();
boolean safeMediaVolumeEnabled =
SystemProperties.getBoolean("audio.safemedia.force", false)
@@ -642,7 +771,7 @@ public class SoundDoseHelper {
// The persisted state is either "disabled" or "active": this is the state applied
// next time we boot and cannot be "inactive"
int persistedState;
- if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) {
+ if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) {
persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
// The state can already be "inactive" here if the user has forced it before
// the 30 seconds timeout for forced configuration. In this case we don't reset
@@ -668,10 +797,6 @@ public class SoundDoseHelper {
/*obj=*/null), /*delay=*/0);
}
}
-
- if (mEnableCsd) {
- initCsd();
- }
}
private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) {
@@ -719,25 +844,32 @@ public class SoundDoseHelper {
mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget();
}
- private int getSafeUsbMediaVolumeIndex() {
+ private int getSafeDeviceMediaVolumeIndex(int deviceType) {
+ // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
+ // instead of computing it from the volume curves
+ if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) {
+ return mSafeMediaVolumeIndex;
+ }
+
// determine UI volume index corresponding to the wanted safe gain in dBFS
int min = MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
int max = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
- mSafeUsbMediaVolumeDbfs = mContext.getResources().getInteger(
+ mSafeMediaVolumeDbfs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_usb_mB) / 100.0f;
while (Math.abs(max - min) > 1) {
int index = (max + min) / 2;
- float gainDB = AudioSystem.getStreamVolumeDB(
- AudioSystem.STREAM_MUSIC, index, AudioSystem.DEVICE_OUT_USB_HEADSET);
+ float gainDB = AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index,
+ deviceType);
if (Float.isNaN(gainDB)) {
//keep last min in case of read error
break;
- } else if (gainDB == mSafeUsbMediaVolumeDbfs) {
+ } else if (gainDB == mSafeMediaVolumeDbfs) {
min = index;
break;
- } else if (gainDB < mSafeUsbMediaVolumeDbfs) {
+ } else if (gainDB < mSafeMediaVolumeDbfs) {
min = index;
} else {
max = index;
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 dce88da6f111..005ad20a2d48 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -23,6 +23,7 @@ import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.hardware.biometrics.AuthenticateOptions;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
@@ -44,8 +45,8 @@ import java.util.function.Supplier;
/**
* A class to keep track of the authentication state for a given client.
*/
-public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
- implements AuthenticationConsumer {
+public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
+ extends AcquisitionClient<T> implements AuthenticationConsumer {
// New, has not started yet
public static final int STATE_NEW = 0;
@@ -89,14 +90,15 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int targetUserId, long operationId, boolean restricted, @NonNull String owner,
- int cookie, boolean requireConfirmation, int sensorId,
+ long operationId, boolean restricted, @NonNull O options,
+ int cookie, boolean requireConfirmation,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
boolean shouldVibrate, int sensorStrength) {
- super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
- shouldVibrate, biometricLogger, biometricContext);
+ super(context, lazyDaemon, token, listener, options.getUserId(),
+ options.getOpPackageName(), cookie, options.getSensorId(), shouldVibrate,
+ biometricLogger, biometricContext);
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
index 0f1fe68ad1d7..25651fa2887e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.util.proto.ProtoOutputStream;
@@ -39,7 +40,7 @@ public interface BiometricServiceProvider<T extends SensorPropertiesInternal> {
List<T> getSensorProperties();
/** Properties for the given sensor id. */
- @NonNull
+ @Nullable
T getSensorProperties(int sensorId);
boolean isHardwareDetected(int sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index 2263e80039bd..a4b0a0eece5b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -92,7 +92,7 @@ public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
final int previousBiometricState = mBiometricState;
if (client instanceof AuthenticationClient) {
- final AuthenticationClient<?> authClient = (AuthenticationClient<?>) client;
+ final AuthenticationClient<?, ?> authClient = (AuthenticationClient<?, ?>) client;
if (authClient.isKeyguard()) {
mBiometricState = STATE_KEYGUARD_AUTH;
} else if (authClient.isBiometricPrompt()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index cdf22aadbd8c..69ad1523118d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -64,7 +64,6 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
- private final List<S> mEnrolledList;
private final boolean mHasEnrollmentsBeforeStarting;
private BaseClientMonitor mCurrentTask;
private boolean mFavorHalEnrollments = false;
@@ -135,13 +134,12 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
+ @NonNull BiometricUtils<S> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
userId, owner, 0 /* cookie */, sensorId, logger, biometricContext);
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
- mEnrolledList = enrolledList;
mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
}
@@ -169,12 +167,16 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
+ final List<S> enrolledList =
+ mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId());
+
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
- getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId(), getLogger(),
+ getOwnerString(), enrolledList, mBiometricUtils, getSensorId(), getLogger(),
getBiometricContext());
- Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
+ Slog.d(TAG, "Starting enumerate: " + mCurrentTask + " enrolledList size:"
+ + enrolledList.size());
mCurrentTask.start(mEnumerateCallback);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 51829684f3ab..fb64bcc3abc1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -64,9 +64,10 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
- mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
+ mFaceService.prepareForAuthentication(requireConfirmation, token, operationId,
sensorReceiver, new FaceAuthenticateOptions.Builder()
.setUserId(userId)
+ .setSensorId(mSensorId)
.setOpPackageName(opPackageName)
.build(),
requestId, cookie, allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 1ee9f53c5774..6d7b2cb40e21 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -246,7 +246,6 @@ public class FaceService extends SystemService {
super.authenticate_enforcePermission();
- final int userId = options.getUserId();
final String opPackageName = options.getOpPackageName();
final boolean restricted = false; // Face APIs are private
final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
@@ -261,9 +260,11 @@ public class FaceService extends SystemService {
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
return -1;
+ } else {
+ options.setSensorId(provider.first);
}
- return provider.second.scheduleAuthenticate(provider.first, token, operationId,
+ return provider.second.scheduleAuthenticate(token, operationId,
0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
restricted, statsClient, isKeyguard);
}
@@ -286,28 +287,27 @@ public class FaceService extends SystemService {
return -1;
}
- return provider.second.scheduleFaceDetect(provider.first, token, options.getUserId(),
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ return provider.second.scheduleFaceDetect(token,
+ new ClientMonitorCallbackConverter(receiver), options,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
- public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
+ public void prepareForAuthentication(boolean requireConfirmation,
IBinder token, long operationId, IBiometricSensorReceiver sensorReceiver,
FaceAuthenticateOptions options, long requestId, int cookie,
boolean allowBackgroundAuthentication) {
super.prepareForAuthentication_enforcePermission();
- final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(options.getSensorId());
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
}
- final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients
final boolean restricted = true; // BiometricPrompt is always restricted
- provider.scheduleAuthenticate(sensorId, token, operationId, cookie,
+ provider.scheduleAuthenticate(token, operationId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), options, requestId,
restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 609c6a77e50a..2cf64b72d01f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -83,18 +83,19 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
- long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
- @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+ long scheduleFaceDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FaceAuthenticateOptions options,
int statsClient);
void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId);
- long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull FaceAuthenticateOptions options,
boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
- void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull FaceAuthenticateOptions options, long requestId,
boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 29dd707a7c1d..976f1cbe1e5c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -28,6 +28,7 @@ import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
+import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -56,7 +57,7 @@ import java.util.function.Supplier;
/**
* Face-specific authentication client for the {@link IFace} AIDL HAL interface.
*/
-class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
+class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
implements LockoutConsumer {
private static final String TAG = "FaceAuthenticationClient";
@@ -80,16 +81,16 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener, long operationId,
+ boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+ boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
@Authenticators.Types int sensorStrength) {
- this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
- restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
- isStrongBiometric, usageStats, lockoutCache /* lockoutCache */,
- allowBackgroundAuthentication,
+ this(context, lazyDaemon, token, requestId, listener, operationId,
+ restricted, options, cookie, requireConfirmation, logger, biometricContext,
+ isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
context.getSystemService(SensorPrivacyManager.class), sensorStrength);
}
@@ -97,15 +98,16 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener, long operationId,
+ boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+ boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
SensorPrivacyManager sensorPrivacyManager,
@Authenticators.Types int biometricStrength) {
- super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ super(context, lazyDaemon, token, listener, operationId, restricted,
+ options, cookie, requireConfirmation, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */,
allowBackgroundAuthentication, false /* shouldVibrate */,
biometricStrength);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 506b2bc8d9db..e65202dca5cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.face.FaceAuthenticateOptions;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -51,11 +52,11 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FaceAuthenticateOptions options,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric) {
- this(context, lazyDaemon, token, requestId, listener, userId, owner, sensorId,
+ this(context, lazyDaemon, token, requestId, listener, options,
logger, biometricContext, isStrongBiometric,
context.getSystemService(SensorPrivacyManager.class));
}
@@ -63,11 +64,12 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements
@VisibleForTesting
FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FaceAuthenticateOptions options,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
- super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ super(context, lazyDaemon, token, listener, options.getUserId(),
+ options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
true /* shouldVibrate */, logger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index b0b23faa9aa5..f09d192966f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -43,10 +43,10 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession>
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricContext biometricContext,
@NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- enrolledList, utils, authenticatorIds);
+ utils, authenticatorIds);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 41e02691ddf8..1a53fec82d98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -410,16 +410,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
- public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
- int userId, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, int statsClient) {
+ public long scheduleFaceDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FaceAuthenticateOptions options, int statsClient) {
final long id = mRequestCounter.incrementAndGet();
+ final int sensorId = options.getSensorId();
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceDetectClient client = new FaceDetectClient(mContext,
mSensors.get(sensorId).getLazySession(),
- token, id, callback, userId, opPackageName, sensorId,
+ token, id, callback, options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -435,18 +436,19 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
- public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull FaceAuthenticateOptions options,
long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
final int userId = options.getUserId();
+ final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
- userId, operationId, restricted, options.getOpPackageName(), cookie,
- false /* requireConfirmation */, sensorId,
+ operationId, restricted, options, cookie,
+ false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
@@ -470,13 +472,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
- public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
- scheduleAuthenticate(sensorId, token, operationId, cookie, callback,
+ scheduleAuthenticate(token, operationId, cookie, callback,
options, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
@@ -595,14 +597,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
mHandler.post(() -> {
- final List<Face> enrolledList = getEnrolledFaces(sensorId, userId);
final FaceInternalCleanupClient client =
new FaceInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- mBiometricContext, enrolledList,
+ mBiometricContext,
FaceUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
if (favorHalEnrollments) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 7e575bc23f9c..1e33c96d50ad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -651,9 +651,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
- public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
- int userId, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, int statsClient) {
+ public long scheduleFaceDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FaceAuthenticateOptions options, int statsClient) {
throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
+ "forget to check the supportsFaceDetection flag?");
}
@@ -665,7 +665,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
- public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter receiver,
@NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
int statsClient, boolean allowBackgroundAuthentication) {
@@ -675,8 +675,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
- mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
- options.getOpPackageName(), cookie, false /* requireConfirmation */, mSensorId,
+ mLazyDaemon, token, requestId, receiver, operationId, restricted,
+ options, cookie, false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric, mLockoutTracker,
mUsageStats, allowBackgroundAuthentication,
@@ -686,13 +686,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
- public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter receiver,
@NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
- scheduleAuthenticate(sensorId, token, operationId, cookie, receiver,
+ scheduleAuthenticate(token, operationId, cookie, receiver,
options, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
@@ -818,12 +818,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId);
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- mBiometricContext, enrolledList,
+ mBiometricContext,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback,
mBiometricStateCallback));
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 1c1f56ccddd5..8ab88923d01e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -25,6 +25,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,7 +51,8 @@ import java.util.function.Supplier;
* Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
* HIDL interface.
*/
-class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
+class FaceAuthenticationClient
+ extends AuthenticationClient<IBiometricsFace, FaceAuthenticateOptions> {
private static final String TAG = "FaceAuthenticationClient";
@@ -67,17 +69,18 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener, long operationId,
+ boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+ boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
@NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
@Authenticators.Types int sensorStrength) {
- super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ super(context, lazyDaemon, token, listener, operationId, restricted,
+ options, cookie, requireConfirmation, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */,
lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
- sensorStrength);
+ sensorStrength);
setRequestId(requestId);
mUsageStats = usageStats;
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
index d21a7501e516..89a17c6b570e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
@@ -42,10 +42,10 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF
FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList,
+ @NonNull BiometricContext biometricContext,
@NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- enrolledList, utils, authenticatorIds);
+ utils, authenticatorIds);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 52d887a75216..d47a57ad6742 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IFingerprintService;
import android.os.IBinder;
import android.os.RemoteException;
@@ -63,8 +64,13 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
- mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
- sensorReceiver, opPackageName, requestId, cookie, allowBackgroundAuthentication);
+ mFingerprintService.prepareForAuthentication(token, operationId, sensorReceiver,
+ new FingerprintAuthenticateOptions.Builder()
+ .setSensorId(mSensorId)
+ .setUserId(userId)
+ .setOpPackageName(opPackageName)
+ .build(),
+ requestId, cookie, allowBackgroundAuthentication);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index affc496edc70..f6c1375730bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -119,7 +119,7 @@ public class FingerprintService extends SystemService {
@NonNull
private final Supplier<String[]> mAidlInstanceNameSupplier;
@NonNull
- private final Function<String, IFingerprint> mIFingerprintProvider;
+ private final Function<String, FingerprintProvider> mFingerprintProvider;
@NonNull
private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
mBiometricStateCallback;
@@ -307,6 +307,8 @@ public class FingerprintService extends SystemService {
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
return -1;
+ } else {
+ options.setSensorId(provider.first);
}
final FingerprintSensorPropertiesInternal sensorProps =
@@ -322,8 +324,8 @@ public class FingerprintService extends SystemService {
return -1;
}
}
- return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
- 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ return provider.second.scheduleAuthenticate(token, operationId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
restricted, statsClient, isKeyguard);
}
@@ -425,40 +427,36 @@ public class FingerprintService extends SystemService {
return -1;
}
- if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, options.getUserId())) {
- // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
- // ever be invoked when the user is encrypted or lockdown.
- Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
- return -1;
- }
-
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");
return -1;
+ } else {
+ options.setSensorId(provider.first);
}
- return provider.second.scheduleFingerDetect(provider.first, token, options.getUserId(),
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ return provider.second.scheduleFingerDetect(token,
+ new ClientMonitorCallbackConverter(receiver), options,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
@Override // Binder call
- public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
- int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
+ public void prepareForAuthentication(IBinder token, long operationId,
+ IBiometricSensorReceiver sensorReceiver,
+ @NonNull FingerprintAuthenticateOptions options,
long requestId, int cookie, boolean allowBackgroundAuthentication) {
super.prepareForAuthentication_enforcePermission();
- final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(options.getSensorId());
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
}
final boolean restricted = true; // BiometricPrompt is always restricted
- provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
- new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+ provider.scheduleAuthenticate(token, operationId, cookie,
+ new ClientMonitorCallbackConverter(sensorReceiver), options, requestId,
restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
allowBackgroundAuthentication);
}
@@ -982,8 +980,7 @@ public class FingerprintService extends SystemService {
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
() -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
- (fqName) -> IFingerprint.Stub.asInterface(
- Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName))));
+ null /* fingerprintProvider */);
}
@VisibleForTesting
@@ -991,16 +988,35 @@ public class FingerprintService extends SystemService {
BiometricContext biometricContext,
Supplier<IBiometricService> biometricServiceSupplier,
Supplier<String[]> aidlInstanceNameSupplier,
- Function<String, IFingerprint> fingerprintProvider) {
+ Function<String, FingerprintProvider> fingerprintProvider) {
super(context);
mBiometricContext = biometricContext;
mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
- mIFingerprintProvider = fingerprintProvider;
mAppOps = context.getSystemService(AppOpsManager.class);
mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+ mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
+ (name) -> {
+ final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
+ final IFingerprint fp = IFingerprint.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ if (fp != null) {
+ try {
+ return new FingerprintProvider(getContext(),
+ mBiometricStateCallback, fp.getSensorProps(), name,
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+ mBiometricContext);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
+ }
+ } else {
+ Slog.e(TAG, "Unable to get declared service: " + fqName);
+ }
+
+ return null;
+ };
mHandler = new Handler(Looper.getMainLooper());
mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1044,23 +1060,9 @@ public class FingerprintService extends SystemService {
final List<ServiceProvider> providers = new ArrayList<>();
for (String instance : instances) {
- final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
- final IFingerprint fp = mIFingerprintProvider.apply(fqName);
-
- if (fp != null) {
- try {
- final FingerprintProvider provider = new FingerprintProvider(getContext(),
- mBiometricStateCallback, fp.getSensorProps(), instance,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
- mBiometricContext);
- Slog.i(TAG, "Adding AIDL provider: " + fqName);
- providers.add(provider);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
- }
- } else {
- Slog.e(TAG, "Unable to get declared service: " + fqName);
- }
+ final FingerprintProvider provider = mFingerprintProvider.apply(instance);
+ Slog.i(TAG, "Adding AIDL provider: " + instance);
+ providers.add(provider);
}
return providers;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 5b6f14d6c805..004af2c2ad62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -78,19 +79,21 @@ public interface ServiceProvider extends
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
- long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
- @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+ long scheduleFingerDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FingerprintAuthenticateOptions options,
int statsClient);
- void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication);
- long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, boolean restricted, int statsClient,
- boolean allowBackgroundAuthentication);
+ @NonNull FingerprintAuthenticateOptions options,
+ boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
void startPreparedClient(int sensorId, int cookie);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index d1a7b1339179..0f81f9f2660e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcqu
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlay;
@@ -65,7 +66,8 @@ import java.util.function.Supplier;
* Fingerprint-specific authentication client supporting the {@link
* android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
+class FingerprintAuthenticationClient
+ extends AuthenticationClient<AidlSession, FingerprintAuthenticateOptions>
implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
private static final int MESSAGE_AUTH_SUCCESS = 2;
@@ -97,13 +99,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull IBinder token,
long requestId,
@NonNull ClientMonitorCallbackConverter listener,
- int targetUserId,
long operationId,
boolean restricted,
- @NonNull String owner,
+ @NonNull FingerprintAuthenticateOptions options,
int cookie,
boolean requireConfirmation,
- int sensorId,
@NonNull BiometricLogger biometricLogger,
@NonNull BiometricContext biometricContext,
boolean isStrongBiometric,
@@ -122,13 +122,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
lazyDaemon,
token,
listener,
- targetUserId,
operationId,
restricted,
- owner,
+ options,
cookie,
requireConfirmation,
- sensorId,
biometricLogger,
biometricContext,
isStrongBiometric,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index f6911ea29837..376d23187fb8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlay;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -52,13 +53,14 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
FingerprintDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull String owner, int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable IUdfpsOverlay udfpsOverlay,
boolean isStrongBiometric) {
- super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ super(context, lazyDaemon, token, listener, options.getUserId(),
+ options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index c315ccf8dea2..ff9127f516af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -45,10 +45,9 @@ class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint
@NonNull Supplier<AidlSession> lazyDaemon,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull List<Fingerprint> enrolledList,
@NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- enrolledList, utils, authenticatorIds);
+ utils, authenticatorIds);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 776d33172e68..23b6f84e6954 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -422,15 +423,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
- public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
- @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+ public long scheduleFingerDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FingerprintAuthenticateOptions options,
int statsClient) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
+ final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
- opPackageName, sensorId,
+ mSensors.get(sensorId).getLazySession(), token, id, callback,
+ options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext,
mUdfpsOverlayController, mUdfpsOverlay, isStrongBiometric);
@@ -441,16 +444,19 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
- public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
- int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+ public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
+ final int userId = options.getUserId();
+ final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
- userId, operationId, restricted, opPackageName, cookie,
- false /* requireConfirmation */, sensorId,
+ operationId, restricted, options, cookie,
+ false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
@@ -485,14 +491,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
- public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
- int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull FingerprintAuthenticateOptions options, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
- scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
- opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
+ scheduleAuthenticate(token, operationId, cookie, callback,
+ options, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
}
@@ -556,7 +562,6 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
mHandler.post(() -> {
- final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
final FingerprintInternalCleanupClient client =
new FingerprintInternalCleanupClient(mContext,
mSensors.get(sensorId).getLazySession(), userId,
@@ -564,7 +569,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
mBiometricContext,
- enrolledList, FingerprintUtils.getInstance(sensorId),
+ FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
if (favorHalEnrollments) {
client.setFavorHalEnrollments();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 4567addc4302..9e6f4e4f13f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -34,6 +34,7 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -631,17 +632,17 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
- public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
+ public long scheduleFingerDetect(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
int statsClient) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
+ scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mLazyDaemon, token, id, listener, userId, opPackageName,
- mSensorProperties.sensorId,
+ mLazyDaemon, token, id, listener, options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, mUdfpsOverlayController, mUdfpsOverlay,
isStrongBiometric);
@@ -652,18 +653,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
- public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
- int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+ public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
+ scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
- restricted, opPackageName, cookie, false /* requireConfirmation */,
- mSensorProperties.sensorId,
+ mContext, mLazyDaemon, token, requestId, listener, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
@@ -675,14 +676,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
- public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
- int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
- scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
- opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
+ scheduleAuthenticate(token, operationId, cookie, listener,
+ options, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
}
@@ -741,14 +742,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final List<Fingerprint> enrolledList = getEnrolledFingerprints(
- mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- mBiometricContext, enrolledList,
+ mBiometricContext,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 73b1288d00d2..0a47c12bbe73 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -22,6 +22,7 @@ import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -362,13 +363,16 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
// Store the authClient parameters so it can be rescheduled
final IBinder token = client.getToken();
final long operationId = authClient.getOperationId();
- final int user = client.getTargetUserId();
final int cookie = client.getCookie();
final ClientMonitorCallbackConverter listener = client.getListener();
- final String opPackageName = client.getOwnerString();
final boolean restricted = authClient.isRestricted();
final int statsClient = client.getLogger().getStatsClient();
final boolean isKeyguard = authClient.isKeyguard();
+ final FingerprintAuthenticateOptions options =
+ new FingerprintAuthenticateOptions.Builder()
+ .setUserId(client.getTargetUserId())
+ .setOpPackageName(client.getOwnerString())
+ .build();
// Don't actually send cancel() to the HAL, since successful auth already finishes
// HAL authenticate() lifecycle. Just
@@ -376,8 +380,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
// Schedule this only after we invoke onClientFinished for the previous client, so that
// internal preemption logic is not run.
- mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
- operationId, user, cookie, listener, opPackageName, restricted, statsClient,
+ mFingerprint21.scheduleAuthenticate(token,
+ operationId, cookie, listener, options, restricted, statsClient,
isKeyguard);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 957005a9223e..d22aef8b3971 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlay;
@@ -57,7 +58,8 @@ import java.util.function.Supplier;
* {@link android.hardware.biometrics.fingerprint.V2_1} and
* {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
*/
-class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint>
+class FingerprintAuthenticationClient
+ extends AuthenticationClient<IBiometricsFingerprint, FingerprintAuthenticateOptions>
implements Udfps {
private static final String TAG = "Biometrics/FingerprintAuthClient";
@@ -72,9 +74,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
FingerprintAuthenticationClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, @NonNull BiometricLogger logger,
+ @NonNull ClientMonitorCallbackConverter listener, long operationId,
+ boolean restricted, @NonNull FingerprintAuthenticateOptions options,
+ int cookie, boolean requireConfirmation, @NonNull BiometricLogger logger,
@NonNull BiometricContext biometricContext, boolean isStrongBiometric,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@@ -84,8 +86,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@Authenticators.Types int sensorStrength) {
- super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
- owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+ super(context, lazyDaemon, token, listener, operationId, restricted,
+ options, cookie, requireConfirmation, logger, biometricContext,
isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
false /* shouldVibrate */, sensorStrength);
setRequestId(requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index cfa9fb429fdd..362c820b9e8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -24,6 +24,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlay;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
@@ -61,12 +62,13 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
public FingerprintDetectClient(@NonNull Context context,
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
@NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable IUdfpsOverlay udfpsOverlay, boolean isStrongBiometric) {
- super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ super(context, lazyDaemon, token, listener, options.getUserId(),
+ options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
true /* shouldVibrate */, biometricLogger, biometricContext);
setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index 5e7cf3578411..8b61f5966c14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -45,11 +45,10 @@ class FingerprintInternalCleanupClient
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
@NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull List<Fingerprint> enrolledList,
@NonNull BiometricUtils<Fingerprint> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
- enrolledList, utils, authenticatorIds);
+ utils, authenticatorIds);
}
@Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index dd92ffcbe472..fab138bb2931 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -18,9 +18,9 @@ package com.android.server.clipboard;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.Context.DEVICE_ID_INVALID;
import android.Manifest;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ab2c002c2b24..1ce917cb7841 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3254,6 +3254,8 @@ public class Vpn {
}
mActiveNetwork = network;
+ mUnderlyingLinkProperties = null;
+ mUnderlyingNetworkCapabilities = null;
mRetryCount = 0;
startOrMigrateIkeSession(network);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 6e1640d545fe..22b6a53ab907 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -35,7 +35,6 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
-import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
@@ -126,7 +125,7 @@ public class BrightnessTracker {
private static final int MSG_BRIGHTNESS_CHANGED = 1;
private static final int MSG_STOP_SENSOR_LISTENER = 2;
private static final int MSG_START_SENSOR_LISTENER = 3;
- private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
+ private static final int MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED = 4;
private static final int MSG_SENSOR_CHANGED = 5;
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -162,7 +161,7 @@ public class BrightnessTracker {
private boolean mColorSamplingEnabled;
private int mNoFramesToSample;
private float mFrameRate;
- private BrightnessConfiguration mBrightnessConfiguration;
+ private boolean mShouldCollectColorSample = false;
// End of block of members that should only be accessed on the mBgHandler thread.
private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
@@ -208,9 +207,9 @@ public class BrightnessTracker {
/**
* Update tracker with new brightness configuration.
*/
- public void setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration) {
- mBgHandler.obtainMessage(MSG_BRIGHTNESS_CONFIG_CHANGED,
- brightnessConfiguration).sendToTarget();
+ public void setShouldCollectColorSample(boolean shouldCollectColorSample) {
+ mBgHandler.obtainMessage(MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED,
+ shouldCollectColorSample).sendToTarget();
}
private void backgroundStart(float initialBrightness) {
@@ -320,7 +319,7 @@ public class BrightnessTracker {
* Notify the BrightnessTracker that the user has changed the brightness of the display.
*/
public void notifyBrightnessChanged(float brightness, boolean userInitiated,
- float powerBrightnessFactor, boolean isUserSetBrightness,
+ float powerBrightnessFactor, boolean wasShortTermModelActive,
boolean isDefaultBrightnessConfig, String uniqueDisplayId, float[] luxValues,
long[] luxTimestamps) {
if (DEBUG) {
@@ -329,7 +328,7 @@ public class BrightnessTracker {
}
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
- powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
+ powerBrightnessFactor, wasShortTermModelActive, isDefaultBrightnessConfig,
mInjector.currentTimeMillis(), uniqueDisplayId, luxValues, luxTimestamps));
m.sendToTarget();
}
@@ -343,7 +342,7 @@ public class BrightnessTracker {
}
private void handleBrightnessChanged(float brightness, boolean userInitiated,
- float powerBrightnessFactor, boolean isUserSetBrightness,
+ float powerBrightnessFactor, boolean wasShortTermModelActive,
boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId,
float[] luxValues, long[] luxTimestamps) {
BrightnessChangeEvent.Builder builder;
@@ -368,7 +367,7 @@ public class BrightnessTracker {
builder.setBrightness(brightness);
builder.setTimeStamp(timestamp);
builder.setPowerBrightnessFactor(powerBrightnessFactor);
- builder.setUserBrightnessPoint(isUserSetBrightness);
+ builder.setUserBrightnessPoint(wasShortTermModelActive);
builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
builder.setUniqueDisplayId(uniqueDisplayId);
@@ -827,8 +826,7 @@ public class BrightnessTracker {
if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
|| !mInjector.isInteractive(mContext)
|| mColorSamplingEnabled
- || mBrightnessConfiguration == null
- || !mBrightnessConfiguration.shouldCollectColorSamples()) {
+ || !mShouldCollectColorSample) {
return;
}
@@ -997,7 +995,7 @@ public class BrightnessTracker {
BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
boolean userInitiatedChange = (msg.arg1 == 1);
handleBrightnessChanged(values.brightness, userInitiatedChange,
- values.powerBrightnessFactor, values.isUserSetBrightness,
+ values.powerBrightnessFactor, values.wasShortTermModelActive,
values.isDefaultBrightnessConfig, values.timestamp,
values.uniqueDisplayId, values.luxValues, values.luxTimestamps);
break;
@@ -1009,14 +1007,11 @@ public class BrightnessTracker {
stopSensorListener();
disableColorSampling();
break;
- case MSG_BRIGHTNESS_CONFIG_CHANGED:
- mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
- boolean shouldCollectColorSamples =
- mBrightnessConfiguration != null
- && mBrightnessConfiguration.shouldCollectColorSamples();
- if (shouldCollectColorSamples && !mColorSamplingEnabled) {
+ case MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED:
+ mShouldCollectColorSample = (boolean) msg.obj;
+ if (mShouldCollectColorSample && !mColorSamplingEnabled) {
enableColorSampling();
- } else if (!shouldCollectColorSamples && mColorSamplingEnabled) {
+ } else if (!mShouldCollectColorSample && mColorSamplingEnabled) {
disableColorSampling();
}
break;
@@ -1031,7 +1026,7 @@ public class BrightnessTracker {
private static class BrightnessChangeValues {
public final float brightness;
public final float powerBrightnessFactor;
- public final boolean isUserSetBrightness;
+ public final boolean wasShortTermModelActive;
public final boolean isDefaultBrightnessConfig;
public final long timestamp;
public final String uniqueDisplayId;
@@ -1039,11 +1034,11 @@ public class BrightnessTracker {
public final long[] luxTimestamps;
BrightnessChangeValues(float brightness, float powerBrightnessFactor,
- boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
+ boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig,
long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) {
this.brightness = brightness;
this.powerBrightnessFactor = powerBrightnessFactor;
- this.isUserSetBrightness = isUserSetBrightness;
+ this.wasShortTermModelActive = wasShortTermModelActive;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.timestamp = timestamp;
this.uniqueDisplayId = uniqueDisplayId;
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 99e709ea3fd8..7b560cecbf21 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -27,6 +27,8 @@ import android.view.DisplayAddress;
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.server.display.mode.DisplayModeDirector;
+
import java.io.PrintWriter;
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d9b350189fc4..75f8accde3a5 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1260,7 +1260,7 @@ public class DisplayDeviceConfig {
return mAmbientDarkeningPercentagesIdle;
}
- SensorData getAmbientLightSensor() {
+ public SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
@@ -2821,7 +2821,7 @@ public class DisplayDeviceConfig {
/**
* Uniquely identifies a Sensor, with the combination of Type and Name.
*/
- static class SensorData {
+ public static class SensorData {
public String type;
public String name;
public float minRefreshRate = 0.0f;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6eb465e1049e..214c5916a8f8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -152,6 +152,7 @@ import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.layout.Layout;
+import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.utils.SensorUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9917bfc3aca6..da8e6a636d72 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1538,10 +1538,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// user, or is a temporary adjustment.
boolean userInitiatedChange = (Float.isNaN(brightnessState))
&& (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
- boolean hadUserBrightnessPoint = false;
+ boolean wasShortTermModelActive = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
- hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
+ wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessState,
mBrightnessConfiguration,
mLastUserSetScreenBrightness,
@@ -1555,7 +1555,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
if (mBrightnessTracker != null) {
- mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null
+ && mBrightnessConfiguration.shouldCollectColorSamples());
}
boolean updateScreenBrightnessSetting = false;
@@ -1823,7 +1824,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
userInitiatedChange = false;
}
notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
- hadUserBrightnessPoint);
+ wasShortTermModelActive);
}
// We save the brightness info *after* the brightness setting has been changed and
@@ -1865,7 +1866,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mTempBrightnessEvent.setRbcStrength(mCdsi != null
? mCdsi.getReduceBrightColorsStrength() : -1);
mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
- mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint);
+ mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive);
// Temporary is what we use during slider interactions. We avoid logging those so that
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
@@ -2634,7 +2635,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
- boolean hadUserDataPoint) {
+ boolean wasShortTermModelActive) {
final float brightnessInNits = convertToNits(brightness);
if (mUseAutoBrightness && brightnessInNits >= 0.0f
&& mAutomaticBrightnessController != null && mBrightnessTracker != null) {
@@ -2645,7 +2646,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
? mPowerRequest.screenLowPowerBrightnessFactor
: 1.0f;
mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
- powerFactor, hadUserDataPoint,
+ powerFactor, wasShortTermModelActive,
mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
mAutomaticBrightnessController.getLastSensorValues(),
mAutomaticBrightnessController.getLastSensorTimestamps());
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index f49419cf789e..a2a53e3f7549 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1250,10 +1250,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// user, or is a temporary adjustment.
boolean userInitiatedChange = (Float.isNaN(brightnessState))
&& (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
- boolean hadUserBrightnessPoint = false;
+ boolean wasShortTermModelActive = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
- hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
+ wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessState,
mBrightnessConfiguration,
mDisplayBrightnessController.getLastUserSetScreenBrightness(),
@@ -1267,7 +1267,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
if (mBrightnessTracker != null) {
- mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null
+ && mBrightnessConfiguration.shouldCollectColorSamples());
}
boolean updateScreenBrightnessSetting = false;
@@ -1537,7 +1538,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
userInitiatedChange = false;
}
notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
- hadUserBrightnessPoint);
+ wasShortTermModelActive);
}
// We save the brightness info *after* the brightness setting has been changed and
@@ -1579,7 +1580,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mTempBrightnessEvent.setRbcStrength(mCdsi != null
? mCdsi.getReduceBrightColorsStrength() : -1);
mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
- mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint);
+ mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive);
mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState
.getDisplayBrightnessStrategyName());
// Temporary is what we use during slider interactions. We avoid logging those so that
@@ -2220,7 +2221,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
- boolean hadUserDataPoint) {
+ boolean wasShortTermModelActive) {
final float brightnessInNits = convertToNits(brightness);
if (mUseAutoBrightness && brightnessInNits >= 0.0f
&& mAutomaticBrightnessController != null && mBrightnessTracker != null) {
@@ -2231,7 +2232,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
? mPowerRequest.screenLowPowerBrightnessFactor
: 1.0f;
mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
- powerFactor, hadUserDataPoint,
+ powerFactor, wasShortTermModelActive,
mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
mAutomaticBrightnessController.getLastSensorValues(),
mAutomaticBrightnessController.getLastSensorTimestamps());
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8f52c97c1f87..58c50346a38f 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index fc90db66c1cb..78c597ea6e53 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -33,6 +33,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import com.android.server.display.layout.Layout;
+import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 8e3460175158..fcaa95731408 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -4,5 +4,7 @@ hackbod@google.com
ogunwale@google.com
santoscordon@google.com
flc@google.com
+wilczynskip@google.com
+brup@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 3e67f0a466d7..2ce7690ecc3f 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -36,6 +36,7 @@ import android.view.SurfaceControl;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.mode.DisplayModeDirector;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 31f5ab7dfe98..24d5ca402dd0 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.display;
+package com.android.server.display.mode;
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
@@ -67,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.SensorUtils;
@@ -214,6 +215,9 @@ public class DisplayModeDirector {
mUdfpsObserver.observe();
}
+ /**
+ * Enables or disables component logging
+ */
public void setLoggingEnabled(boolean loggingEnabled) {
if (mLoggingEnabled == loggingEnabled) {
return;
@@ -1574,7 +1578,10 @@ public class DisplayModeDirector {
}
}
- final class AppRequestObserver {
+ /**
+ * Responsible for keeping track of app requested refresh rates per display
+ */
+ public final class AppRequestObserver {
private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
@@ -1583,6 +1590,9 @@ public class DisplayModeDirector {
mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
}
+ /**
+ * Sets refresh rates from app request
+ */
public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
float requestedMaxRefreshRateRange) {
synchronized (mLock) {
@@ -1665,7 +1675,7 @@ public class DisplayModeDirector {
return null;
}
- public void dumpLocked(PrintWriter pw) {
+ private void dumpLocked(PrintWriter pw) {
pw.println(" AppRequestObserver");
pw.println(" mAppRequestedModeByDisplay:");
for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
@@ -1794,7 +1804,7 @@ public class DisplayModeDirector {
*/
@VisibleForTesting
public class BrightnessObserver implements DisplayManager.DisplayListener {
- private final static int LIGHT_SENSOR_RATE_MS = 250;
+ private static final int LIGHT_SENSOR_RATE_MS = 250;
private int[] mLowDisplayBrightnessThresholds;
private int[] mLowAmbientBrightnessThresholds;
private int[] mHighDisplayBrightnessThresholds;
@@ -2019,7 +2029,7 @@ public class DisplayModeDirector {
return mLowAmbientBrightnessThresholds;
}
- public void observe(SensorManager sensorManager) {
+ private void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
@@ -2064,11 +2074,11 @@ public class DisplayModeDirector {
mDeviceConfigDisplaySettings.startListening();
mInjector.registerDisplayListener(this, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_CHANGED |
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
}
- public void setLoggingEnabled(boolean loggingEnabled) {
+ private void setLoggingEnabled(boolean loggingEnabled) {
if (mLoggingEnabled == loggingEnabled) {
return;
}
@@ -2076,7 +2086,8 @@ public class DisplayModeDirector {
mLightSensorListener.setLoggingEnabled(loggingEnabled);
}
- public void onRefreshRateSettingChangedLocked(float min, float max) {
+ @VisibleForTesting
+ void onRefreshRateSettingChangedLocked(float min, float max) {
boolean changeable = (max - min > 1f && max > 60f);
if (mRefreshRateChangeable != changeable) {
mRefreshRateChangeable = changeable;
@@ -2089,14 +2100,14 @@ public class DisplayModeDirector {
}
}
- public void onLowPowerModeEnabledLocked(boolean b) {
+ private void onLowPowerModeEnabledLocked(boolean b) {
if (mLowPowerModeEnabled != b) {
mLowPowerModeEnabled = b;
updateSensorStatus();
}
}
- public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
+ private void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
int[] ambientThresholds) {
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
@@ -2123,7 +2134,7 @@ public class DisplayModeDirector {
}
}
- public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
+ private void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
int[] ambientThresholds) {
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
@@ -2150,7 +2161,7 @@ public class DisplayModeDirector {
}
}
- public void dumpLocked(PrintWriter pw) {
+ void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
@@ -2400,7 +2411,7 @@ public class DisplayModeDirector {
}
@VisibleForTesting
- public void setDefaultDisplayState(int state) {
+ void setDefaultDisplayState(int state) {
if (mLoggingEnabled) {
Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
+ mDefaultDisplayState + ", state = " + state);
@@ -2475,7 +2486,7 @@ public class DisplayModeDirector {
}
private final class LightSensorEventListener implements SensorEventListener {
- final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
+ private static final int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
private long mTimestamp;
private boolean mLoggingEnabled;
@@ -2876,10 +2887,10 @@ public class DisplayModeDirector {
}
final int hbmMode = info.highBrightnessMode;
- final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF &&
- info.adjustedBrightness > info.highBrightnessTransitionPoint;
- if (hbmMode == mHbmMode.get(displayId) &&
- isHbmActive == mHbmActive.get(displayId)) {
+ final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
+ && info.adjustedBrightness > info.highBrightnessTransitionPoint;
+ if (hbmMode == mHbmMode.get(displayId)
+ && isHbmActive == mHbmActive.get(displayId)) {
// no change, ignore.
return;
}
@@ -2901,7 +2912,7 @@ public class DisplayModeDirector {
Vote vote = null;
if (mHbmActive.get(displayId, false)) {
final int hbmMode =
- mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+ mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
// Device resource properties take priority over DisplayDeviceConfig
if (mRefreshRateInHbmSunlight > 0) {
@@ -2909,7 +2920,7 @@ public class DisplayModeDirector {
mRefreshRateInHbmSunlight);
} else {
final List<RefreshRateLimitation> limits =
- mDisplayManagerInternal.getRefreshRateLimitations(displayId);
+ mDisplayManagerInternal.getRefreshRateLimitations(displayId);
for (int i = 0; limits != null && i < limits.size(); i++) {
final RefreshRateLimitation limitation = limits.get(i);
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
@@ -2919,8 +2930,8 @@ public class DisplayModeDirector {
}
}
}
- } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR &&
- mRefreshRateInHbmHdr > 0) {
+ } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ && mRefreshRateInHbmHdr > 0) {
// HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for
// a vote from Device properties
vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
@@ -2988,9 +2999,6 @@ public class DisplayModeDirector {
}
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
- public DeviceConfigDisplaySettings() {
- }
-
public void startListening() {
mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), this);
@@ -3001,8 +3009,8 @@ public class DisplayModeDirector {
*/
public int[] getLowDisplayBrightnessThresholds() {
return getIntArrayProperty(
- DisplayManager.DeviceConfig.
- KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
}
/*
@@ -3010,8 +3018,8 @@ public class DisplayModeDirector {
*/
public int[] getLowAmbientBrightnessThresholds() {
return getIntArrayProperty(
- DisplayManager.DeviceConfig.
- KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
public int getRefreshRateInLowZone() {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 3bc4b543750e..3e2efdd7db85 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -158,15 +158,14 @@ final class DreamController {
final DreamRecord oldDream = mCurrentDream;
mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
if (oldDream != null) {
- if (!oldDream.mWakingGently) {
- // We will stop these previous dreams once the new dream is started.
- mPreviousDreams.add(oldDream);
- } else if (Objects.equals(oldDream.mName, mCurrentDream.mName)) {
+ if (Objects.equals(oldDream.mName, mCurrentDream.mName)) {
// We are attempting to start a dream that is currently waking up gently.
// Let's silently stop the old instance here to clear the dream state.
// This should happen after the new mCurrentDream is set to avoid announcing
// a "dream stopped" state.
stopDreamInstance(/* immediately */ true, "restarting same dream", oldDream);
+ } else {
+ mPreviousDreams.add(oldDream);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index da65f27688d8..2efb0beaa567 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -24,13 +24,14 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.util.Log;
import android.view.inputmethod.ImeTracker;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.view.IImeTracker;
import java.io.PrintWriter;
import java.time.Instant;
@@ -53,7 +54,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings("GuardedBy")
public final class ImeTrackerService extends IImeTracker.Stub {
- static final String TAG = "ImeTrackerService";
+ private static final String TAG = ImeTracker.TAG;
/** The threshold in milliseconds after which a history entry is considered timed out. */
private static final long TIMEOUT_MS = 10_000;
@@ -71,67 +72,71 @@ public final class ImeTrackerService extends IImeTracker.Stub {
@NonNull
@Override
- public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
- @SoftInputShowHideReason int reason) {
- final IBinder binder = new Binder();
- final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_SHOW,
- ImeTracker.STATUS_RUN, origin, reason);
+ public synchronized ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ final var binder = new Binder();
+ final var token = new ImeTracker.Token(binder, tag);
+ final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
+ origin, reason);
mHistory.addEntry(binder, entry);
// Register a delayed task to handle the case where the new entry times out.
mHandler.postDelayed(() -> {
synchronized (ImeTrackerService.this) {
- mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
+ mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
}
}, TIMEOUT_MS);
- return binder;
+ return token;
}
@NonNull
@Override
- public synchronized IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
- @SoftInputShowHideReason int reason) {
- final IBinder binder = new Binder();
- final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_HIDE,
- ImeTracker.STATUS_RUN, origin, reason);
+ public synchronized ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ final var binder = new Binder();
+ final var token = new ImeTracker.Token(binder, tag);
+ final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
+ origin, reason);
mHistory.addEntry(binder, entry);
// Register a delayed task to handle the case where the new entry times out.
mHandler.postDelayed(() -> {
synchronized (ImeTrackerService.this) {
- mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
+ mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
}
}, TIMEOUT_MS);
- return binder;
+ return token;
}
@Override
- public synchronized void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
- final History.Entry entry = mHistory.getEntry(statsToken);
+ public synchronized void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
+ final var entry = mHistory.getEntry(binder);
if (entry == null) return;
entry.mPhase = phase;
}
@Override
- public synchronized void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+ public synchronized void onFailed(@NonNull ImeTracker.Token statsToken,
+ @ImeTracker.Phase int phase) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
}
@Override
- public synchronized void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+ public synchronized void onCancelled(@NonNull ImeTracker.Token statsToken,
+ @ImeTracker.Phase int phase) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
}
@Override
- public synchronized void onShown(@NonNull IBinder statsToken) {
+ public synchronized void onShown(@NonNull ImeTracker.Token statsToken) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
}
@Override
- public synchronized void onHidden(@NonNull IBinder statsToken) {
+ public synchronized void onHidden(@NonNull ImeTracker.Token statsToken) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
}
@@ -141,9 +146,9 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* @param statsToken the token corresponding to the current IME request.
* @param requestWindowName the name of the window that created the IME request.
*/
- public synchronized void onImmsUpdate(@NonNull IBinder statsToken,
+ public synchronized void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
@NonNull String requestWindowName) {
- final History.Entry entry = mHistory.getEntry(statsToken);
+ final var entry = mHistory.getEntry(statsToken.getBinder());
if (entry == null) return;
entry.mRequestWindowName = requestWindowName;
@@ -181,17 +186,17 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** Latest entry sequence number. */
private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
- /** Adds a live entry. */
+ /** Adds a live entry corresponding to the given IME tracking token's binder. */
@GuardedBy("ImeTrackerService.this")
- private void addEntry(@NonNull IBinder statsToken, @NonNull Entry entry) {
- mLiveEntries.put(statsToken, entry);
+ private void addEntry(@NonNull IBinder binder, @NonNull Entry entry) {
+ mLiveEntries.put(binder, entry);
}
- /** Gets the entry corresponding to the given IME tracking token, if it exists. */
+ /** Gets the entry corresponding to the given IME tracking token's binder, if it exists. */
@Nullable
@GuardedBy("ImeTrackerService.this")
- private Entry getEntry(@NonNull IBinder statsToken) {
- return mLiveEntries.get(statsToken);
+ private Entry getEntry(@NonNull IBinder binder) {
+ return mLiveEntries.get(binder);
}
/**
@@ -204,10 +209,21 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
*/
@GuardedBy("ImeTrackerService.this")
- private void setFinished(@NonNull IBinder statsToken, @ImeTracker.Status int status,
- @ImeTracker.Phase int phase) {
- final Entry entry = mLiveEntries.remove(statsToken);
- if (entry == null) return;
+ private void setFinished(@NonNull ImeTracker.Token statsToken,
+ @ImeTracker.Status int status, @ImeTracker.Phase int phase) {
+ final var entry = mLiveEntries.remove(statsToken.getBinder());
+ if (entry == null) {
+ // This will be unconditionally called through the postDelayed above to handle
+ // potential timeouts, and is thus intentionally dropped to avoid having to manually
+ // save and remove the registered callback. Only timeout calls are expected.
+ if (status != ImeTracker.STATUS_TIMEOUT) {
+ Log.i(TAG, statsToken.getTag()
+ + ": setFinished on previously finished token at "
+ + ImeTracker.Debug.phaseToString(phase) + " with "
+ + ImeTracker.Debug.statusToString(status));
+ }
+ return;
+ }
entry.mDuration = System.currentTimeMillis() - entry.mStartTime;
entry.mStatus = status;
@@ -216,6 +232,13 @@ public final class ImeTrackerService extends IImeTracker.Stub {
entry.mPhase = phase;
}
+ if (status == ImeTracker.STATUS_TIMEOUT) {
+ // All events other than timeouts are already logged in the client-side ImeTracker.
+ Log.i(TAG, statsToken.getTag() + ": setFinished at "
+ + ImeTracker.Debug.phaseToString(entry.mPhase) + " with "
+ + ImeTracker.Debug.statusToString(status));
+ }
+
// Remove excess entries overflowing capacity (plus one for the new entry).
while (mEntries.size() >= CAPACITY) {
mEntries.remove();
@@ -232,21 +255,22 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** Dumps the contents of the circular buffer. */
@GuardedBy("ImeTrackerService.this")
private void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
- final DateTimeFormatter formatter =
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
- .withZone(ZoneId.systemDefault());
+ final var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
+ .withZone(ZoneId.systemDefault());
pw.print(prefix);
- pw.println("ImeTrackerService#History.mLiveEntries:");
+ pw.println("ImeTrackerService#History.mLiveEntries: "
+ + mLiveEntries.size() + " elements");
- for (final Entry entry: mLiveEntries.values()) {
+ for (final var entry: mLiveEntries.values()) {
dumpEntry(entry, pw, prefix, formatter);
}
pw.print(prefix);
- pw.println("ImeTrackerService#History.mEntries:");
+ pw.println("ImeTrackerService#History.mEntries: "
+ + mEntries.size() + " elements");
- for (final Entry entry: mEntries) {
+ for (final var entry: mEntries) {
dumpEntry(entry, pw, prefix, formatter);
}
}
@@ -255,34 +279,22 @@ public final class ImeTrackerService extends IImeTracker.Stub {
private void dumpEntry(@NonNull Entry entry, @NonNull PrintWriter pw,
@NonNull String prefix, @NonNull DateTimeFormatter formatter) {
pw.print(prefix);
- pw.println("ImeTrackerService#History #" + entry.mSequenceNumber + ":");
-
- pw.print(prefix);
- pw.println(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
-
- pw.print(prefix);
- pw.println(" duration=" + entry.mDuration + "ms");
+ pw.print(" #" + entry.mSequenceNumber);
+ pw.print(" " + ImeTracker.Debug.typeToString(entry.mType));
+ pw.print(" - " + ImeTracker.Debug.statusToString(entry.mStatus));
+ pw.print(" - " + entry.mTag);
+ pw.println(" (" + entry.mDuration + "ms):");
pw.print(prefix);
- pw.print(" type=" + ImeTracker.Debug.typeToString(entry.mType));
+ pw.print(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
+ pw.println(" " + ImeTracker.Debug.originToString(entry.mOrigin));
pw.print(prefix);
- pw.print(" status=" + ImeTracker.Debug.statusToString(entry.mStatus));
+ pw.print(" reason=" + InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
+ pw.println(" " + ImeTracker.Debug.phaseToString(entry.mPhase));
pw.print(prefix);
- pw.print(" origin="
- + ImeTracker.Debug.originToString(entry.mOrigin));
-
- pw.print(prefix);
- pw.print(" reason="
- + InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
-
- pw.print(prefix);
- pw.print(" phase="
- + ImeTracker.Debug.phaseToString(entry.mPhase));
-
- pw.print(prefix);
- pw.print(" requestWindowName=" + entry.mRequestWindowName);
+ pw.println(" requestWindowName=" + entry.mRequestWindowName);
}
/** A history entry. */
@@ -291,6 +303,10 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** The entry's sequence number in the history. */
private final int mSequenceNumber = sSequenceNumber.getAndIncrement();
+ /** Logging tag, of the shape "component:random_hexadecimal". */
+ @NonNull
+ private final String mTag;
+
/** Uid of the client that requested the IME. */
private final int mUid;
@@ -323,13 +339,15 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/**
* Name of the window that created the IME request.
*
- * Note: This is later set through {@link #onImmsUpdate(IBinder, String)}.
+ * Note: This is later set through {@link #onImmsUpdate}.
*/
@NonNull
private String mRequestWindowName = "not set";
- private Entry(int uid, @ImeTracker.Type int type, @ImeTracker.Status int status,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ private Entry(@NonNull String tag, int uid, @ImeTracker.Type int type,
+ @ImeTracker.Status int status, @ImeTracker.Origin int origin,
+ @SoftInputShowHideReason int reason) {
+ mTag = tag;
mUid = uid;
mType = type;
mStatus = status;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 0ea64abbe548..8c7658e53dcd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -178,6 +178,14 @@ public abstract class InputMethodManagerInternal {
public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
/**
+ * Switch the keyboard layout in response to a keyboard shortcut.
+ *
+ * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the
+ * previous subtype.
+ */
+ public abstract void switchKeyboardLayout(int direction);
+
+ /**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
private static final InputMethodManagerInternal NOP =
@@ -256,6 +264,10 @@ public abstract class InputMethodManagerInternal {
@Override
public void maybeFinishStylusHandwriting() {
}
+
+ @Override
+ public void switchKeyboardLayout(int direction) {
+ }
};
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8ef4e4afae9b..d397a0c5e0bc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -145,6 +145,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethod;
@@ -168,7 +169,6 @@ import com.android.internal.os.TransferPipe;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.view.IImeTracker;
import com.android.internal.view.IInputMethodManager;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags;
@@ -532,6 +532,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
/**
* The client that is currently bound to an input method.
*/
+ @Nullable
private ClientState mCurClient;
/**
@@ -557,11 +558,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
int mCurFocusedWindowSoftInputMode;
/**
- * The client by which {@link #mCurFocusedWindow} was reported.
+ * The client by which {@link #mCurFocusedWindow} was reported. This gets updated whenever an
+ * IME-focusable window gained focus (without necessarily starting an input connection),
+ * while {@link #mCurClient} only gets updated when we actually start an input connection.
+ *
+ * @see #mCurFocusedWindow
*/
+ @Nullable
ClientState mCurFocusedWindowClient;
/**
+ * The editor info by which {@link #mCurFocusedWindow} was reported. This differs from
+ * {@link #mCurEditorInfo} the same way {@link #mCurFocusedWindowClient} differs
+ * from {@link #mCurClient}.
+ *
+ * @see #mCurFocusedWindow
+ */
+ @Nullable
+ EditorInfo mCurFocusedWindowEditorInfo;
+
+ /**
* The {@link IRemoteInputConnection} last provided by the current client.
*/
IRemoteInputConnection mCurInputConnection;
@@ -580,6 +596,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
/**
* The {@link EditorInfo} last provided by the current client.
*/
+ @Nullable
EditorInfo mCurEditorInfo;
/**
@@ -1393,6 +1410,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ boolean changed = false;
+ for (InputMethodInfo imi : mMethodList) {
+ if (imi.getPackageName().equals(packageName)) {
+ mAdditionalSubtypeMap.remove(imi.getId());
+ changed = true;
+ }
+ }
+ if (changed) {
+ AdditionalSubtypeUtils.save(
+ mAdditionalSubtypeMap, mMethodMap, mSettings.getCurrentUserId());
+ mChangedPackages.add(packageName);
+ }
+ }
+
+ @Override
public void onFinishPackageChanges() {
onFinishPackageChangesInternal();
clearPackageChangeState();
@@ -2062,7 +2095,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
- methodList, directBootAwareness);
+ methodList, directBootAwareness, mSettings.getEnabledInputMethodNames());
settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */);
}
// filter caller's access to input methods
@@ -2265,6 +2298,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (mCurFocusedWindowClient == cs) {
mCurFocusedWindowClient = null;
+ mCurFocusedWindowEditorInfo = null;
}
}
}
@@ -3245,7 +3279,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
notifyInputMethodSubtypeChangedLocked(userId, info, null);
return;
}
- if (newSubtype != oldSubtype) {
+ if (!newSubtype.equals(oldSubtype)) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
@@ -3453,10 +3487,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
- // TODO(b/261565259): to avoid using null, add package name in ClientState
- final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
- final int uid = mCurClient != null ? mCurClient.mUid : -1;
- statsToken = ImeTracker.forLogging().onRequestShow(packageName, uid,
+ statsToken = createStatsTokenForFocusedClient(true /* show */,
ImeTracker.ORIGIN_SERVER_START_INPUT, reason);
}
@@ -3530,17 +3561,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
- // TODO(b/261565259): to avoid using null, add package name in ClientState
- final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
- final int uid;
- if (mCurClient != null) {
- uid = mCurClient.mUid;
- } else if (mCurFocusedWindowClient != null) {
- uid = mCurFocusedWindowClient.mUid;
- } else {
- uid = -1;
- }
- statsToken = ImeTracker.forLogging().onRequestHide(packageName, uid,
+ statsToken = createStatsTokenForFocusedClient(false /* show */,
ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
}
@@ -3775,6 +3796,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mCurFocusedWindow = windowToken;
mCurFocusedWindowSoftInputMode = softInputMode;
mCurFocusedWindowClient = cs;
+ mCurFocusedWindowEditorInfo = editorInfo;
mCurPerceptible = true;
// We want to start input before showing the IME, but after closing
@@ -4044,15 +4066,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (!calledWithValidTokenLocked(token)) {
return false;
}
- final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype);
- if (nextSubtype == null) {
- return false;
- }
- setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
- nextSubtype.mSubtypeId);
- return true;
+ return switchToNextInputMethodLocked(token, onlyCurrentIme);
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
+ private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
+ final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
+ onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype);
+ if (nextSubtype == null) {
+ return false;
}
+ setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
+ nextSubtype.mSubtypeId);
+ return true;
}
@BinderThread
@@ -4130,7 +4157,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
- methodList, DirectBootAwareness.AUTO);
+ methodList, DirectBootAwareness.AUTO, mSettings.getEnabledInputMethodNames());
final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap,
userId, false);
settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
@@ -4703,13 +4730,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mWindowManagerInternal.onToggleImeRequested(
show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
- mCurFocusedWindowClient, mCurEditorInfo, info.focusedWindowName,
+ mCurFocusedWindowClient, mCurFocusedWindowEditorInfo, info.focusedWindowName,
mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
info.imeSurfaceParentName));
if (statsToken != null) {
- mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+ mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
}
}
@@ -5027,7 +5054,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
static void queryInputMethodServicesInternal(Context context,
@UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
- @DirectBootAwareness int directBootAwareness) {
+ @DirectBootAwareness int directBootAwareness, List<String> enabledInputMethodList) {
final Context userAwareContext = context.getUserId() == userId
? context
: context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
@@ -5060,6 +5087,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
methodList.ensureCapacity(services.size());
methodMap.ensureCapacity(services.size());
+ filterInputMethodServices(additionalSubtypeMap, methodMap, methodList,
+ enabledInputMethodList, userAwareContext, services);
+ }
+
+ static void filterInputMethodServices(
+ ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
+ ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+ List<String> enabledInputMethodList, Context userAwareContext,
+ List<ResolveInfo> services) {
+ final ArrayMap<String, Integer> imiPackageCount = new ArrayMap<>();
+
for (int i = 0; i < services.size(); ++i) {
ResolveInfo ri = services.get(i);
ServiceInfo si = ri.serviceInfo;
@@ -5079,10 +5117,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (imi.isVrOnly()) {
continue; // Skip VR-only IME, which isn't supported for now.
}
- methodList.add(imi);
- methodMap.put(imi.getId(), imi);
- if (DEBUG) {
- Slog.d(TAG, "Found an input method " + imi);
+ final String packageName = si.packageName;
+ // only include IMEs which are from the system, enabled, or below the threshold
+ if (si.applicationInfo.isSystemApp() || enabledInputMethodList.contains(imi.getId())
+ || imiPackageCount.getOrDefault(packageName, 0)
+ < InputMethodInfo.MAX_IMES_PER_PACKAGE) {
+ imiPackageCount.put(packageName,
+ 1 + imiPackageCount.getOrDefault(packageName, 0));
+
+ methodList.add(imi);
+ methodMap.put(imi.getId(), imi);
+ if (DEBUG) {
+ Slog.d(TAG, "Found an input method " + imi);
+ }
+ } else if (DEBUG) {
+ Slog.d(TAG, "Found an input method, but ignored due threshold: " + imi);
}
} catch (Exception e) {
Slog.wtf(TAG, "Unable to load input method " + imeId, e);
@@ -5104,7 +5153,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mMyPackageMonitor.clearKnownImePackageNamesLocked();
queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
- mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO);
+ mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO,
+ mSettings.getEnabledInputMethodNames());
// Construct the set of possible IME packages for onPackageChanged() to avoid false
// negatives when the package state remains to be the same but only the component state is
@@ -5163,7 +5213,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
reenableMinimumNonAuxSystemImes);
final int numImes = defaultEnabledIme.size();
for (int i = 0; i < numImes; ++i) {
- final InputMethodInfo imi = defaultEnabledIme.get(i);
+ final InputMethodInfo imi = defaultEnabledIme.get(i);
if (DEBUG) {
Slog.d(TAG, "--- enable ime = " + imi);
}
@@ -5463,7 +5513,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList, DirectBootAwareness.AUTO);
+ methodMap, methodList, DirectBootAwareness.AUTO,
+ mSettings.getEnabledInputMethodNames());
return methodMap;
}
@@ -5719,6 +5770,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mHandler.removeMessages(MSG_FINISH_HANDWRITING);
mHandler.obtainMessage(MSG_FINISH_HANDWRITING).sendToTarget();
}
+
+ @Override
+ public void switchKeyboardLayout(int direction) {
+ synchronized (ImfLock.class) {
+ if (direction > 0) {
+ switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */);
+ } else {
+ // TODO(b/258853866): Support backwards switching.
+ }
+ }
+ }
}
@BinderThread
@@ -5751,9 +5813,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// We cannot simply distinguish a bad IME that reports an arbitrary package name from
// an unfortunate IME whose internal state is already obsolete due to the asynchronous
// nature of our system. Let's compare it with our internal record.
- if (!TextUtils.equals(mCurEditorInfo.packageName, packageName)) {
+ final var curPackageName = mCurEditorInfo != null
+ ? mCurEditorInfo.packageName : null;
+ if (!TextUtils.equals(curPackageName, packageName)) {
Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName="
- + mCurEditorInfo.packageName + " packageName=" + packageName);
+ + curPackageName + " packageName=" + packageName);
return null;
}
// This user ID can never bee spoofed.
@@ -6421,7 +6485,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList, DirectBootAwareness.AUTO);
+ methodMap, methodList, DirectBootAwareness.AUTO,
+ mSettings.getEnabledInputMethodNames());
final InputMethodSettings settings = new InputMethodSettings(mContext,
methodMap, userId, false);
@@ -6514,6 +6579,30 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mImeTrackerService;
}
+ /**
+ * Creates an IME request tracking token for the current focused client.
+ *
+ * @param show whether this is a show or a hide request.
+ * @param origin the origin of the IME request.
+ * @param reason the reason why the IME request was created.
+ */
+ @NonNull
+ private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
+ @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+ final int uid = mCurFocusedWindowClient != null
+ ? mCurFocusedWindowClient.mUid
+ : -1;
+ final var packageName = mCurFocusedWindowEditorInfo != null
+ ? mCurFocusedWindowEditorInfo.packageName
+ : "uid(" + uid + ")";
+
+ if (show) {
+ return ImeTracker.forLogging().onRequestShow(packageName, uid, origin, reason);
+ } else {
+ return ImeTracker.forLogging().onRequestHide(packageName, uid, origin, reason);
+ }
+ }
+
private static final class InputMethodPrivilegedOperationsImpl
extends IInputMethodPrivilegedOperations.Stub {
private final InputMethodManagerService mImms;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 559eb5341632..17536fcb820e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -438,6 +438,15 @@ final class InputMethodUtils {
mSubtypeSplitter);
}
+ List<String> getEnabledInputMethodNames() {
+ List<String> result = new ArrayList<>();
+ for (Pair<String, ArrayList<String>> pair :
+ getEnabledInputMethodsAndSubtypeListLocked()) {
+ result.add(pair.first);
+ }
+ return result;
+ }
+
void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
if (reloadInputMethodStr) {
getEnabledInputMethodsStr();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4013468481ab..5a832b78487c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -420,7 +420,7 @@ public class LockSettingsService extends ILockSettings.Stub {
try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) {
setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
/* isLockTiedToParent= */ true);
- tieProfileLockToParent(profileUserId, unifiedProfilePassword);
+ tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword);
mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword);
}
}
@@ -1065,18 +1065,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave");
}
- private static final String[] UNPROTECTED_SETTINGS = {
- // These three LOCK_PATTERN_* settings have traditionally been readable via the public API
- // android.provider.Settings.{System,Secure}.getString() without any permission.
- Settings.Secure.LOCK_PATTERN_ENABLED,
- Settings.Secure.LOCK_PATTERN_VISIBLE,
- Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED,
- };
-
private final void checkDatabaseReadPermission(String requestedKey, int userId) {
- if (ArrayUtils.contains(UNPROTECTED_SETTINGS, requestedKey)) {
- return;
- }
if (!hasPermission(PERMISSION)) {
throw new SecurityException("uid=" + getCallingUid() + " needs permission "
+ PERMISSION + " to read " + requestedKey + " for user " + userId);
@@ -1190,9 +1179,6 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public boolean getBoolean(String key, boolean defaultValue, int userId) {
checkDatabaseReadPermission(key, userId);
- if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
- return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN;
- }
return mStorage.getBoolean(key, defaultValue, userId);
}
@@ -1887,34 +1873,43 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
- protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
- if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
+ protected void tieProfileLockToParent(int profileUserId, int parentUserId,
+ LockscreenCredential password) {
+ if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + profileUserId);
final byte[] iv;
final byte[] ciphertext;
+ final long parentSid;
+ try {
+ parentSid = getGateKeeperService().getSecureUserId(parentUserId);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to talk to GateKeeper service", e);
+ }
+
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
try {
mJavaKeyStore.setEntry(
- PROFILE_KEY_NAME_ENCRYPT + userId,
+ PROFILE_KEY_NAME_ENCRYPT + profileUserId,
new java.security.KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
mJavaKeyStore.setEntry(
- PROFILE_KEY_NAME_DECRYPT + userId,
+ PROFILE_KEY_NAME_DECRYPT + profileUserId,
new java.security.KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
+ .setBoundToSpecificSecureUserId(parentSid)
.setUserAuthenticationValidityDurationSeconds(30)
.build());
// Key imported, obtain a reference to it.
SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
- PROFILE_KEY_NAME_ENCRYPT + userId, null);
+ PROFILE_KEY_NAME_ENCRYPT + profileUserId, null);
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+ KeyProperties.ENCRYPTION_PADDING_NONE);
@@ -1923,7 +1918,7 @@ public class LockSettingsService extends ILockSettings.Stub {
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + userId);
+ mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
}
} catch (UnrecoverableKeyException
| BadPaddingException | IllegalBlockSizeException | KeyStoreException
@@ -1933,7 +1928,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (iv.length != PROFILE_KEY_IV_SIZE) {
throw new IllegalArgumentException("Invalid iv length: " + iv.length);
}
- mStorage.writeChildProfileLock(userId, ArrayUtils.concat(iv, ciphertext));
+ mStorage.writeChildProfileLock(profileUserId, ArrayUtils.concat(iv, ciphertext));
}
private void setUserKeyProtection(@UserIdInt int userId, byte[] secret) {
@@ -2045,7 +2040,7 @@ public class LockSettingsService extends ILockSettings.Stub {
LockscreenCredential piUserDecryptedPassword = profileUserDecryptedPasswords.get(i);
if (piUserId != -1 && piUserDecryptedPassword != null) {
if (DEBUG) Slog.v(TAG, "Restore tied profile lock");
- tieProfileLockToParent(piUserId, piUserDecryptedPassword);
+ tieProfileLockToParent(piUserId, userId, piUserDecryptedPassword);
}
if (piUserDecryptedPassword != null) {
piUserDecryptedPassword.zeroize();
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index e592a2207095..d070b416c53c 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -780,6 +780,10 @@ class SyntheticPasswordManager {
int slot = loadWeaverSlot(protectorId, userId);
destroyState(WEAVER_SLOT_NAME, protectorId, userId);
if (slot != INVALID_WEAVER_SLOT) {
+ if (!isWeaverAvailable()) {
+ Slog.e(TAG, "Cannot erase Weaver slot because Weaver is unavailable");
+ return;
+ }
Set<Integer> usedSlots = getUsedWeaverSlots();
if (!usedSlots.contains(slot)) {
Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
new file mode 100644
index 000000000000..58fdb57af8fa
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
+import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
+import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.MediaRoute2Info;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Controls bluetooth routes and provides selected route override.
+ *
+ * <p>The controller offers similar functionality to {@link LegacyBluetoothRouteController} but does
+ * not support routes selection logic. Instead, relies on external clients to make a decision
+ * about currently selected route.
+ *
+ * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
+ * Policies.
+ */
+class AudioPoliciesBluetoothRouteController implements BluetoothRouteController {
+ private static final String TAG = "APBtRouteController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
+ private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
+
+ // Maps hardware address to BluetoothRouteInfo
+ private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
+ private final List<BluetoothRouteInfo> mActiveRoutes = new ArrayList<>();
+
+ // Route type -> volume map
+ private final SparseIntArray mVolumeMap = new SparseIntArray();
+
+ private final Context mContext;
+ private final BluetoothAdapter mBluetoothAdapter;
+ private final BluetoothRoutesUpdatedListener mListener;
+ private final AudioManager mAudioManager;
+ private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
+
+ private final AdapterStateChangedReceiver mAdapterStateChangedReceiver =
+ new AdapterStateChangedReceiver();
+ private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
+ new DeviceStateChangedReceiver();
+
+ private BluetoothA2dp mA2dpProfile;
+ private BluetoothHearingAid mHearingAidProfile;
+ private BluetoothLeAudio mLeAudioProfile;
+
+ AudioPoliciesBluetoothRouteController(Context context, BluetoothAdapter btAdapter,
+ BluetoothRoutesUpdatedListener listener) {
+ mContext = context;
+ mBluetoothAdapter = btAdapter;
+ mListener = listener;
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ buildBluetoothRoutes();
+ }
+
+ /**
+ * Registers listener to bluetooth status changes as the provided user.
+ *
+ * The registered receiver listens to {@link BluetoothA2dp#ACTION_ACTIVE_DEVICE_CHANGED} and
+ * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED } events for {@link BluetoothProfile#A2DP},
+ * {@link BluetoothProfile#HEARING_AID}, and {@link BluetoothProfile#LE_AUDIO} bluetooth profiles.
+ *
+ * @param user {@code UserHandle} as which receiver is registered
+ */
+ @Override
+ public void start(UserHandle user) {
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
+
+ IntentFilter adapterStateChangedIntentFilter = new IntentFilter();
+
+ adapterStateChangedIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiverAsUser(mAdapterStateChangedReceiver, user,
+ adapterStateChangedIntentFilter, null, null);
+
+ IntentFilter deviceStateChangedIntentFilter = new IntentFilter();
+
+ deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
+ deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ deviceStateChangedIntentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+ deviceStateChangedIntentFilter.addAction(
+ BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+ deviceStateChangedIntentFilter.addAction(
+ BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
+ deviceStateChangedIntentFilter.addAction(
+ BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+
+ mContext.registerReceiverAsUser(mDeviceStateChangedReceiver, user,
+ deviceStateChangedIntentFilter, null, null);
+ }
+
+ @Override
+ public void stop() {
+ mContext.unregisterReceiver(mAdapterStateChangedReceiver);
+ mContext.unregisterReceiver(mDeviceStateChangedReceiver);
+ }
+
+ @Override
+ public boolean selectRoute(String deviceAddress) {
+ // Temporary no-op.
+ return false;
+ }
+
+ /**
+ * Transfers to a given bluetooth route.
+ * The dedicated BT device with the route would be activated.
+ *
+ * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
+ * BT routes.
+ */
+ @Override
+ public void transferTo(@Nullable String routeId) {
+ if (routeId == null) {
+ clearActiveDevices();
+ return;
+ }
+
+ BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId);
+
+ if (btRouteInfo == null) {
+ Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId);
+ return;
+ }
+
+ if (mBluetoothAdapter != null) {
+ mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
+ }
+ }
+
+ private BluetoothRouteInfo findBluetoothRouteWithRouteId(String routeId) {
+ if (routeId == null) {
+ return null;
+ }
+ for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
+ if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
+ return btRouteInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Clears the active device for all known profiles.
+ */
+ private void clearActiveDevices() {
+ if (mBluetoothAdapter != null) {
+ mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
+ }
+ }
+
+ private void buildBluetoothRoutes() {
+ mBluetoothRoutes.clear();
+ Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
+ if (bondedDevices != null) {
+ for (BluetoothDevice device : bondedDevices) {
+ if (device.isConnected()) {
+ BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+ if (newBtRoute.mConnectedProfiles.size() > 0) {
+ mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+ }
+ }
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public MediaRoute2Info getSelectedRoute() {
+ // For now, active routes can be multiple only when a pair of hearing aid devices is active.
+ // Let the first active device represent them.
+ return (mActiveRoutes.isEmpty() ? null : mActiveRoutes.get(0).mRoute);
+ }
+
+ @NonNull
+ @Override
+ public List<MediaRoute2Info> getTransferableRoutes() {
+ List<MediaRoute2Info> routes = getAllBluetoothRoutes();
+ for (BluetoothRouteInfo btRoute : mActiveRoutes) {
+ routes.remove(btRoute.mRoute);
+ }
+ return routes;
+ }
+
+ @NonNull
+ @Override
+ public List<MediaRoute2Info> getAllBluetoothRoutes() {
+ List<MediaRoute2Info> routes = new ArrayList<>();
+ List<String> routeIds = new ArrayList<>();
+
+ MediaRoute2Info selectedRoute = getSelectedRoute();
+ if (selectedRoute != null) {
+ routes.add(selectedRoute);
+ routeIds.add(selectedRoute.getId());
+ }
+
+ for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ // A pair of hearing aid devices or having the same hardware address
+ if (routeIds.contains(btRoute.mRoute.getId())) {
+ continue;
+ }
+ routes.add(btRoute.mRoute);
+ routeIds.add(btRoute.mRoute.getId());
+ }
+ return routes;
+ }
+
+ /**
+ * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}.
+ *
+ * @return true if devices can be handled by the provider.
+ */
+ @Override
+ public boolean updateVolumeForDevices(int devices, int volume) {
+ int routeType;
+ if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
+ routeType = MediaRoute2Info.TYPE_HEARING_AID;
+ } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
+ | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
+ routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) {
+ routeType = MediaRoute2Info.TYPE_BLE_HEADSET;
+ } else {
+ return false;
+ }
+ mVolumeMap.put(routeType, volume);
+
+ boolean shouldNotify = false;
+ for (BluetoothRouteInfo btRoute : mActiveRoutes) {
+ if (btRoute.mRoute.getType() != routeType) {
+ continue;
+ }
+ btRoute.mRoute = new MediaRoute2Info.Builder(btRoute.mRoute)
+ .setVolume(volume)
+ .build();
+ shouldNotify = true;
+ }
+ if (shouldNotify) {
+ notifyBluetoothRoutesUpdated();
+ }
+ return true;
+ }
+
+ private void notifyBluetoothRoutesUpdated() {
+ if (mListener != null) {
+ mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
+ }
+ }
+
+ private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
+ BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
+ newBtRoute.mBtDevice = device;
+
+ String routeId = device.getAddress();
+ String deviceName = device.getName();
+ if (TextUtils.isEmpty(deviceName)) {
+ deviceName = mContext.getResources().getText(R.string.unknownName).toString();
+ }
+ int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ newBtRoute.mConnectedProfiles = new SparseBooleanArray();
+ if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
+ newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true);
+ }
+ if (mHearingAidProfile != null
+ && mHearingAidProfile.getConnectedDevices().contains(device)) {
+ newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
+ // Intentionally assign the same ID for a pair of devices to publish only one of them.
+ routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
+ type = MediaRoute2Info.TYPE_HEARING_AID;
+ }
+ if (mLeAudioProfile != null
+ && mLeAudioProfile.getConnectedDevices().contains(device)) {
+ newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
+ routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device);
+ type = MediaRoute2Info.TYPE_BLE_HEADSET;
+ }
+
+ // Current volume will be set when connected.
+ newBtRoute.mRoute = new MediaRoute2Info.Builder(routeId, deviceName)
+ .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
+ .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
+ .setDescription(mContext.getResources().getText(
+ R.string.bluetooth_a2dp_audio_route_name).toString())
+ .setType(type)
+ .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setAddress(device.getAddress())
+ .build();
+ return newBtRoute;
+ }
+
+ private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
+ @MediaRoute2Info.ConnectionState int state) {
+ if (btRoute == null) {
+ Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
+ return;
+ }
+ if (btRoute.mRoute.getConnectionState() == state) {
+ return;
+ }
+
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.mRoute)
+ .setConnectionState(state);
+ builder.setType(btRoute.getRouteType());
+
+ if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
+ builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0));
+ }
+ btRoute.mRoute = builder.build();
+ }
+
+ private void addActiveRoute(BluetoothRouteInfo btRoute) {
+ if (btRoute == null) {
+ Slog.w(TAG, "addActiveRoute: btRoute is null");
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Adding active route: " + btRoute.mRoute);
+ }
+ if (mActiveRoutes.contains(btRoute)) {
+ Slog.w(TAG, "addActiveRoute: btRoute is already added.");
+ return;
+ }
+ setRouteConnectionState(btRoute, STATE_CONNECTED);
+ mActiveRoutes.add(btRoute);
+ }
+
+ private void removeActiveRoute(BluetoothRouteInfo btRoute) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing active route: " + btRoute.mRoute);
+ }
+ if (mActiveRoutes.remove(btRoute)) {
+ setRouteConnectionState(btRoute, STATE_DISCONNECTED);
+ }
+ }
+
+ private void clearActiveRoutesWithType(int type) {
+ if (DEBUG) {
+ Log.d(TAG, "Clearing active routes with type. type=" + type);
+ }
+ Iterator<BluetoothRouteInfo> iter = mActiveRoutes.iterator();
+ while (iter.hasNext()) {
+ BluetoothRouteInfo btRoute = iter.next();
+ if (btRoute.mRoute.getType() == type) {
+ iter.remove();
+ setRouteConnectionState(btRoute, STATE_DISCONNECTED);
+ }
+ }
+ }
+
+ private void addActiveDevices(BluetoothDevice device) {
+ // Let the given device be the first active device
+ BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
+ // This could happen if ACTION_ACTIVE_DEVICE_CHANGED is sent before
+ // ACTION_CONNECTION_STATE_CHANGED is sent.
+ if (activeBtRoute == null) {
+ activeBtRoute = createBluetoothRoute(device);
+ mBluetoothRoutes.put(device.getAddress(), activeBtRoute);
+ }
+ addActiveRoute(activeBtRoute);
+
+ // A bluetooth route with the same route ID should be added.
+ for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ if (TextUtils.equals(btRoute.mRoute.getId(), activeBtRoute.mRoute.getId())
+ && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
+ activeBtRoute.mBtDevice.getAddress())) {
+ addActiveRoute(btRoute);
+ }
+ }
+ }
+
+ private static class BluetoothRouteInfo {
+ private BluetoothDevice mBtDevice;
+ private MediaRoute2Info mRoute;
+ private SparseBooleanArray mConnectedProfiles;
+
+ @MediaRoute2Info.Type
+ int getRouteType() {
+ // Let hearing aid profile have a priority.
+ if (mConnectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
+ return MediaRoute2Info.TYPE_HEARING_AID;
+ }
+
+ if (mConnectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) {
+ return MediaRoute2Info.TYPE_BLE_HEADSET;
+ }
+
+ return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+ }
+ }
+
+ // These callbacks run on the main thread.
+ private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ List<BluetoothDevice> activeDevices;
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = (BluetoothA2dp) proxy;
+ // It may contain null.
+ activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP);
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = (BluetoothHearingAid) proxy;
+ activeDevices = mBluetoothAdapter.getActiveDevices(
+ BluetoothProfile.HEARING_AID);
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = (BluetoothLeAudio) proxy;
+ activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
+ break;
+ default:
+ return;
+ }
+ for (BluetoothDevice device : proxy.getConnectedDevices()) {
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (btRoute == null) {
+ btRoute = createBluetoothRoute(device);
+ mBluetoothRoutes.put(device.getAddress(), btRoute);
+ }
+ if (activeDevices.contains(device)) {
+ addActiveRoute(btRoute);
+ }
+ }
+ notifyBluetoothRoutesUpdated();
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = null;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = null;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ private class AdapterStateChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ if (state == BluetoothAdapter.STATE_OFF
+ || state == BluetoothAdapter.STATE_TURNING_OFF) {
+ mBluetoothRoutes.clear();
+ notifyBluetoothRoutesUpdated();
+ } else if (state == BluetoothAdapter.STATE_ON) {
+ buildBluetoothRoutes();
+ if (!mBluetoothRoutes.isEmpty()) {
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+ }
+
+ private class DeviceStateChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ BluetoothDevice device = intent.getParcelableExtra(
+ BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
+
+ switch (intent.getAction()) {
+ case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+ if (device != null) {
+ addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
+ }
+ notifyBluetoothRoutesUpdated();
+ break;
+ case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_HEARING_AID);
+ if (device != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting active hearing aid devices. device=" + device);
+ }
+
+ addActiveDevices(device);
+ }
+ notifyBluetoothRoutesUpdated();
+ break;
+ case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
+ clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET);
+ if (device != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting active le audio devices. device=" + device);
+ }
+
+ addActiveDevices(device);
+ }
+ notifyBluetoothRoutesUpdated();
+ break;
+ case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
+ handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
+ break;
+ case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
+ handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
+ break;
+ case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
+ handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device);
+ break;
+ }
+ }
+
+ private void handleConnectionStateChanged(int profile, Intent intent,
+ BluetoothDevice device) {
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ if (btRoute == null) {
+ btRoute = createBluetoothRoute(device);
+ if (btRoute.mConnectedProfiles.size() > 0) {
+ mBluetoothRoutes.put(device.getAddress(), btRoute);
+ notifyBluetoothRoutesUpdated();
+ }
+ } else {
+ btRoute.mConnectedProfiles.put(profile, true);
+ }
+ } else if (state == BluetoothProfile.STATE_DISCONNECTING
+ || state == BluetoothProfile.STATE_DISCONNECTED) {
+ if (btRoute != null) {
+ btRoute.mConnectedProfiles.delete(profile);
+ if (btRoute.mConnectedProfiles.size() == 0) {
+ removeActiveRoute(mBluetoothRoutes.remove(device.getAddress()));
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index 08691d035f29..d4a118458952 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -68,6 +68,17 @@ import java.util.Objects;
*/
void stop();
+
+ /**
+ * Selects the route with the given {@code deviceAddress}.
+ *
+ * @param deviceAddress The physical address of the device to select. May be null to unselect
+ * the currently selected device.
+ * @return Whether the selection succeeds. If the selection fails, the state of the instance
+ * remains unaltered.
+ */
+ boolean selectRoute(@Nullable String deviceAddress);
+
/**
* Transfers Bluetooth output to the given route.
*
@@ -145,6 +156,12 @@ import java.util.Objects;
}
@Override
+ public boolean selectRoute(String deviceAddress) {
+ // no op
+ return false;
+ }
+
+ @Override
public void transferTo(String routeId) {
// no op
}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
new file mode 100644
index 000000000000..8bd6416a6ddb
--- /dev/null
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_DOCK;
+import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.IAudioService;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Controls device routes.
+ *
+ * <p>A device route is a system wired route, for example, built-in speaker, wired
+ * headsets and headphones, dock, hdmi, or usb devices.
+ *
+ * <p>Thread safe.
+ *
+ * @see SystemMediaRoute2Provider
+ */
+/* package */ final class DeviceRouteController {
+
+ private static final String TAG = "WiredRoutesController";
+
+ private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final AudioManager mAudioManager;
+ @NonNull
+ private final IAudioService mAudioService;
+
+ @NonNull
+ private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ @NonNull
+ private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+
+ private int mDeviceVolume;
+ private MediaRoute2Info mDeviceRoute;
+
+ /* package */ static DeviceRouteController createInstance(@NonNull Context context,
+ @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ AudioManager audioManager = context.getSystemService(AudioManager.class);
+ IAudioService audioService = IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
+
+ return new DeviceRouteController(context,
+ audioManager,
+ audioService,
+ onDeviceRouteChangedListener);
+ }
+
+ @VisibleForTesting
+ /* package */ DeviceRouteController(@NonNull Context context,
+ @NonNull AudioManager audioManager,
+ @NonNull IAudioService audioService,
+ @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(audioManager);
+ Objects.requireNonNull(audioService);
+ Objects.requireNonNull(onDeviceRouteChangedListener);
+
+ mContext = context;
+ mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+
+ mAudioManager = audioManager;
+ mAudioService = audioService;
+
+ AudioRoutesInfo newAudioRoutes = null;
+ try {
+ newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
+ }
+
+ mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ }
+
+ @NonNull
+ /* package */ synchronized MediaRoute2Info getDeviceRoute() {
+ return mDeviceRoute;
+ }
+
+ /* package */ synchronized boolean updateVolume(int volume) {
+ if (mDeviceVolume == volume) {
+ return false;
+ }
+
+ mDeviceVolume = volume;
+ mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+ .setVolume(volume)
+ .build();
+
+ return true;
+ }
+
+ private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
+ int name = R.string.default_audio_route_name;
+ int type = TYPE_BUILTIN_SPEAKER;
+
+ if (newRoutes != null) {
+ if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
+ type = TYPE_WIRED_HEADPHONES;
+ name = com.android.internal.R.string.default_audio_route_name_headphones;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
+ type = TYPE_WIRED_HEADSET;
+ name = com.android.internal.R.string.default_audio_route_name_headphones;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+ type = TYPE_DOCK;
+ name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
+ type = TYPE_HDMI;
+ name = com.android.internal.R.string.default_audio_route_name_external_device;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
+ type = TYPE_USB_DEVICE;
+ name = com.android.internal.R.string.default_audio_route_name_usb;
+ }
+ }
+
+ synchronized (this) {
+ return new MediaRoute2Info.Builder(
+ DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
+ .setVolumeHandling(mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
+ .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setType(type)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LIVE_VIDEO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+ .build();
+ }
+ }
+
+ private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+ }
+
+ /* package */ interface OnDeviceRouteChangedListener {
+ void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute);
+ }
+
+ private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+
+ @Override
+ public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
+ MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ synchronized (DeviceRouteController.this) {
+ mDeviceRoute = deviceRoute;
+ }
+ notifyDeviceRouteUpdate(deviceRoute);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index 7979d2a3fb3b..e31a7fc5d5ff 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -52,7 +52,7 @@ import java.util.Map;
import java.util.Set;
class LegacyBluetoothRouteController implements BluetoothRouteController {
- private static final String TAG = "BTRouteProvider";
+ private static final String TAG = "LBtRouteProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
@@ -132,6 +132,12 @@ class LegacyBluetoothRouteController implements BluetoothRouteController {
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
+ @Override
+ public boolean selectRoute(String deviceAddress) {
+ // No-op as the class decides if a route is selected based on Bluetooth events.
+ return false;
+ }
+
/**
* Transfers to a given bluetooth route.
* The dedicated BT device with the route would be activated.
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 76ff19fcb322..638e81a5df36 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,25 +16,12 @@
package com.android.server.media;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
-import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
-import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
-import static android.media.MediaRoute2Info.TYPE_DOCK;
-import static android.media.MediaRoute2Info.TYPE_HDMI;
-import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
-
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
@@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import java.util.Objects;
@@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
SystemMediaRoute2Provider.class.getName());
static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
- static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
private final AudioManager mAudioManager;
- private final IAudioService mAudioService;
private final Handler mHandler;
private final Context mContext;
private final UserHandle mUser;
+
+ private final DeviceRouteController mDeviceRouteController;
private final BluetoothRouteController mBtRouteProvider;
private String mSelectedRouteId;
// For apps without MODIFYING_AUDIO_ROUTING permission.
// This should be the currently selected route.
MediaRoute2Info mDefaultRoute;
- MediaRoute2Info mDeviceRoute;
RoutingSessionInfo mDefaultSessionInfo;
- int mDeviceVolume;
private final AudioManagerBroadcastReceiver mAudioReceiver =
new AudioManagerBroadcastReceiver();
@@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
- final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
- @Override
- public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
- mHandler.post(() -> {
- updateDeviceRoute(newRoutes);
- notifyProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- }
- };
-
SystemMediaRoute2Provider(Context context, UserHandle user) {
super(COMPONENT_NAME);
mIsSystemRouteProvider = true;
@@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mHandler = new Handler(Looper.getMainLooper());
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mAudioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> {
publishProviderState();
@@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
});
- AudioRoutesInfo newAudioRoutes = null;
- try {
- newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
- } catch (RemoteException e) {
- }
+ mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> {
+ mHandler.post(() -> {
+ publishProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
+ });
+ });
- // The methods below should be called after all fields are initialized, as they
+ // These methods below should be called after all fields are initialized, as they
// access the fields inside.
- updateDeviceRoute(newAudioRoutes);
+ updateProviderState();
updateSessionInfosIfNeeded();
}
@@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
// The currently selected route is the default route.
return;
}
- if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
+
+ MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
+ if (TextUtils.equals(routeId, deviceRoute.getId())) {
mBtRouteProvider.transferTo(null);
} else {
mBtRouteProvider.transferTo(routeId);
@@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (mSessionInfos.isEmpty()) {
return null;
}
+
+ MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
+
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
SYSTEM_SESSION_ID, packageName).setSystemSession(true);
- builder.addSelectedRoute(mDeviceRoute.getId());
+ builder.addSelectedRoute(deviceRoute.getId());
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
builder.addTransferableRoute(route.getId());
}
@@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
}
- private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
- int name = R.string.default_audio_route_name;
- int type = TYPE_BUILTIN_SPEAKER;
- if (newRoutes != null) {
- if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
- type = TYPE_WIRED_HEADPHONES;
- name = com.android.internal.R.string.default_audio_route_name_headphones;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
- type = TYPE_WIRED_HEADSET;
- name = com.android.internal.R.string.default_audio_route_name_headphones;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
- type = TYPE_DOCK;
- name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
- type = TYPE_HDMI;
- name = com.android.internal.R.string.default_audio_route_name_external_device;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
- type = TYPE_USB_DEVICE;
- name = com.android.internal.R.string.default_audio_route_name_usb;
- }
- }
-
- mDeviceRoute = new MediaRoute2Info.Builder(
- DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
- .setVolumeHandling(mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
- updateProviderState();
- }
-
private void updateProviderState() {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
- builder.addRoute(mDeviceRoute);
+
+ // We must have a device route in the provider info.
+ builder.addRoute(mDeviceRouteController.getDeviceRoute());
+
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
builder.addRoute(route);
}
@@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
SYSTEM_SESSION_ID, "" /* clientPackageName */)
.setSystemSession(true);
- MediaRoute2Info selectedRoute = mDeviceRoute;
+ MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
+ MediaRoute2Info selectedRoute = deviceRoute;
MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
if (selectedBtRoute != null) {
selectedRoute = selectedBtRoute;
- builder.addTransferableRoute(mDeviceRoute.getId());
+ builder.addTransferableRoute(deviceRoute.getId());
}
mSelectedRouteId = selectedRoute.getId();
mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
@@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
return;
}
- if (mDeviceVolume != volume) {
- mDeviceVolume = volume;
- mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
- .setVolume(volume)
- .build();
- }
+
+ mDeviceRouteController.updateVolume(volume);
+
publishProviderState();
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index faa06f70d411..9bca9f0192b2 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -404,7 +404,7 @@ public final class BackgroundDexOptService {
"BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
() -> {
TimingsTraceAndSlog tr =
- new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
+ new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_DALVIK);
tr.traceBegin("jobExecution");
boolean completed = false;
boolean fatalError = false;
@@ -494,6 +494,8 @@ public final class BackgroundDexOptService {
@GuardedBy("mLock")
private void waitForDexOptThreadToFinishLocked() {
TimingsTraceAndSlog tr = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
+ // This tracing section doesn't have any correspondence in ART Service - it never waits for
+ // cancellation to finish.
tr.traceBegin("waitForDexOptThreadToFinishLocked");
try {
// Wait but check in regular internal to see if the thread is still alive.
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index fac77486cd19..c232b3698bc0 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -678,8 +678,4 @@ public interface Computer extends PackageDataSnapshot {
@NonNull
Collection<SharedUserSetting> getAllSharedUsers();
-
- boolean isChangeEnabled(long changeId, int uid);
-
- boolean isChangeEnabled(long changeId, ApplicationInfo info);
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 21f661b4f21f..5984360a534c 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -125,7 +125,6 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.compat.PlatformCompat;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
@@ -575,7 +574,8 @@ public class ComputerEngine implements Computer {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- this, list, false, intent, resolvedType, filterCallingUid);
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, intent, resolvedType, filterCallingUid);
}
}
} else {
@@ -591,7 +591,7 @@ public class ComputerEngine implements Computer {
String callingPkgName = getInstantAppPackageName(filterCallingUid);
boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
lockedResult.result = maybeAddInstantAppInstaller(
- lockedResult.result, intent, resolvedType, flags, filterCallingUid,
+ lockedResult.result, intent, resolvedType, flags,
userId, resolveForStart, isRequesterInstantApp);
}
if (lockedResult.sortResult) {
@@ -604,7 +604,8 @@ public class ComputerEngine implements Computer {
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- this, list, false, originalIntent, resolvedType, filterCallingUid);
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, originalIntent, resolvedType, filterCallingUid);
}
return skipPostResolution ? list : applyPostResolutionFilter(
@@ -686,7 +687,8 @@ public class ComputerEngine implements Computer {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- this, list, false, intent, resolvedType, callingUid);
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, intent, resolvedType, callingUid);
}
}
} else {
@@ -697,7 +699,8 @@ public class ComputerEngine implements Computer {
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- this, list, false, originalIntent, resolvedType, callingUid);
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, originalIntent, resolvedType, callingUid);
}
return list;
@@ -710,7 +713,7 @@ public class ComputerEngine implements Computer {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
- resolvedType, flags, callingUid, userId);
+ resolvedType, flags, userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
@@ -720,7 +723,7 @@ public class ComputerEngine implements Computer {
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
- resolvedType, flags, pkg.getServices(), callingUid,
+ resolvedType, flags, pkg.getServices(),
userId);
if (resolveInfos == null) {
return Collections.emptyList();
@@ -750,7 +753,7 @@ public class ComputerEngine implements Computer {
{@link PackageManager.SKIP_CURRENT_PROFILE} set.
*/
result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
- intent, resolvedType, flags, filterCallingUid, userId), userId));
+ intent, resolvedType, flags, userId), userId));
}
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/, flags);
@@ -774,7 +777,7 @@ public class ComputerEngine implements Computer {
|| !shouldFilterApplication(setting, filterCallingUid, userId))) {
result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this,
intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
- filterCallingUid, userId), userId));
+ userId), userId));
}
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
@@ -1105,7 +1108,7 @@ public class ComputerEngine implements Computer {
return null;
}
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
- resolvedType, flags, Binder.getCallingUid(), parentUserId);
+ resolvedType, flags, parentUserId);
if (resultTargetUser == null || resultTargetUser.isEmpty()) {
return null;
@@ -1343,7 +1346,7 @@ public class ComputerEngine implements Computer {
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
- int callingUid, int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
+ int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
ResolveInfo localInstantApp = null;
@@ -1356,7 +1359,6 @@ public class ComputerEngine implements Computer {
| PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
- callingUid,
userId);
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
@@ -3691,13 +3693,10 @@ public class ComputerEngine implements Computer {
ps, callingUid, component, TYPE_ACTIVITY, userId, true /* filterUninstall */)) {
return false;
}
- final boolean callerBlocksNullAction = isChangeEnabled(
- IntentFilter.BLOCK_NULL_ACTION_INTENTS, callingUid);
for (int i=0; i< a.getIntents().size(); i++) {
if (a.getIntents().get(i).getIntentFilter()
.match(intent.getAction(), resolvedType, intent.getScheme(),
- intent.getData(), intent.getCategories(), TAG,
- /*supportWildcards*/ false, callerBlocksNullAction, null, null) >= 0) {
+ intent.getData(), intent.getCategories(), TAG) >= 0) {
return true;
}
}
@@ -5795,37 +5794,4 @@ public class ComputerEngine implements Computer {
public UserInfo[] getUserInfos() {
return mInjector.getUserManagerInternal().getUserInfos();
}
-
- @Override
- public boolean isChangeEnabled(long changeId, int uid) {
- final PlatformCompat compat = mInjector.getCompatibility();
- int appId = UserHandle.getAppId(uid);
- SettingBase s = mSettings.getSettingBase(appId);
- if (s instanceof PackageSetting) {
- var ps = (PackageSetting) s;
- if (ps.getAndroidPackage() == null) return false;
- var info = new ApplicationInfo();
- info.packageName = ps.getPackageName();
- info.targetSdkVersion = ps.getAndroidPackage().getTargetSdkVersion();
- return compat.isChangeEnabledInternal(changeId, info);
- } else if (s instanceof SharedUserSetting) {
- var ss = (SharedUserSetting) s;
- List<AndroidPackage> packages = ss.getPackages();
- for (int i = 0; i < packages.size(); ++i) {
- var pkg = packages.get(i);
- var info = new ApplicationInfo();
- info.packageName = pkg.getPackageName();
- info.targetSdkVersion = pkg.getTargetSdkVersion();
- if (compat.isChangeEnabledInternal(changeId, info)) {
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public boolean isChangeEnabled(long changeId, ApplicationInfo info) {
- return mInjector.getCompatibility().isChangeEnabledInternal(changeId, info);
- }
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java
index ee8f5602fad2..90d89c606686 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java
@@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
-import android.os.Binder;
import android.util.SparseBooleanArray;
import com.android.internal.app.IntentForwarderActivity;
@@ -279,7 +278,7 @@ public final class DefaultCrossProfileResolver extends CrossProfileResolver {
}
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(computer, intent,
- resolvedType, flags, Binder.getCallingUid(), targetUserId);
+ resolvedType, flags, targetUserId);
if (CollectionUtils.isEmpty(resultTargetUser)) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 0fd81ac0bc41..a9d4115b4b79 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -16,7 +16,7 @@
package com.android.server.pm;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.Trace.TRACE_TAG_DALVIK;
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
import static com.android.server.pm.ApexManager.ActiveApexInfo;
@@ -470,11 +470,11 @@ public final class DexOptHelper {
@DexOptResult
private int performDexOptTraced(DexoptOptions options) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt");
try {
return performDexOptInternal(options);
} finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DALVIK);
}
}
@@ -605,7 +605,7 @@ public final class DexOptHelper {
throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName);
}
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt");
// Whoever is calling forceDexOpt wants a compiled package.
// Don't use profiles since that may cause compilation to be skipped.
@@ -615,7 +615,7 @@ public final class DexOptHelper {
@DexOptResult int res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DALVIK);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
throw new IllegalStateException("Failed to dexopt: " + res);
}
diff --git a/services/core/java/com/android/server/pm/NoFilteringResolver.java b/services/core/java/com/android/server/pm/NoFilteringResolver.java
index e53e756b29bd..392389009398 100644
--- a/services/core/java/com/android/server/pm/NoFilteringResolver.java
+++ b/services/core/java/com/android/server/pm/NoFilteringResolver.java
@@ -102,7 +102,7 @@ public class NoFilteringResolver extends CrossProfileResolver {
boolean hasNonNegativePriorityResult,
Function<String, PackageStateInternal> pkgSettingFunction) {
List<ResolveInfo> resolveInfos = mComponentResolver.queryActivities(computer,
- intent, resolvedType, flags, Binder.getCallingUid(), targetUserId);
+ intent, resolvedType, flags, targetUserId);
List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
if (resolveInfos != null) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index f33813759e4f..0a90e7a30db6 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -386,7 +386,7 @@ public class PackageDexOptimizer {
options.getCompilationReason());
// OTAPreopt doesn't have stats so don't report in that case.
if (packageStats != null) {
- Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dex2oat-metrics");
try {
long sessionId = sRandom.nextLong();
ArtStatsLogUtils.writeStatsLog(
@@ -403,7 +403,7 @@ public class PackageDexOptimizer {
dexCodeIsa,
path);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a42e78b69709..de5f0c4d523f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6675,24 +6675,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Deprecated
public void legacyDumpProfiles(String packageName, boolean dumpClassesAndMethods)
throws LegacyDexoptDisabledException {
- /* Only the shell, root, or the app user should be able to dump profiles. */
- final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshotComputer();
- final String[] callerPackageNames = snapshot.getPackagesForUid(callingUid);
- if (!PackageManagerServiceUtils.isRootOrShell(callingUid)
- && !ArrayUtils.contains(callerPackageNames, packageName)) {
- throw new SecurityException("dumpProfiles");
- }
-
AndroidPackage pkg = snapshot.getPackage(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
synchronized (mInstallLock) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dump profiles");
mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9036d4c0615b..928ffa718c6f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
@@ -95,6 +94,7 @@ import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
import com.android.server.LocalManagerRegistry;
import com.android.server.Watchdog;
+import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -1166,8 +1166,10 @@ public class PackageManagerServiceUtils {
return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
+ // Static to give access to ComputeEngine
public static void applyEnforceIntentFilterMatching(
- Computer computer, List<ResolveInfo> resolveInfos, boolean isReceiver,
+ PlatformCompat compat, ComponentResolverApi resolver,
+ List<ResolveInfo> resolveInfos, boolean isReceiver,
Intent intent, String resolvedType, int filterCallingUid) {
if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return;
@@ -1175,11 +1177,6 @@ public class PackageManagerServiceUtils {
? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
: null;
- final boolean callerBlocksNullAction = computer.isChangeEnabled(
- BLOCK_NULL_ACTION_INTENTS, filterCallingUid);
-
- final ComponentResolverApi resolver = computer.getComponentResolver();
-
for (int i = resolveInfos.size() - 1; i >= 0; --i) {
final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
@@ -1190,15 +1187,11 @@ public class PackageManagerServiceUtils {
}
// Only enforce filter matching if target app's target SDK >= T
- if (!computer.isChangeEnabled(
+ if (!compat.isChangeEnabledInternal(
ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) {
continue;
}
- // Block null action intent if either source or target app's target SDK >= U
- final boolean blockNullAction = callerBlocksNullAction
- || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, info.applicationInfo);
-
final ParsedMainComponent comp;
if (info instanceof ActivityInfo) {
if (isReceiver) {
@@ -1220,8 +1213,7 @@ public class PackageManagerServiceUtils {
boolean match = false;
for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
- if (IntentResolver.intentMatchesFilter(
- intentFilter, intent, resolvedType, blockNullAction)) {
+ if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
match = true;
break;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 93b0dcba0afe..586e1123fdc4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -391,6 +391,11 @@ class PackageManagerShellCommand extends ShellCommand {
private int runLegacyDexoptCommand(@NonNull String cmd)
throws RemoteException, LegacyDexoptDisabledException {
Installer.checkLegacyDexoptDisabled();
+
+ if (!PackageManagerServiceUtils.isRootOrShell(Binder.getCallingUid())) {
+ throw new SecurityException("Dexopt shell commands need root or shell access");
+ }
+
switch (cmd) {
case "compile":
return runCompile();
@@ -4410,10 +4415,9 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
}
pw.println(" --reset: restore package to its post-install state");
- pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
+ pw.println(" --check-prof (true | false): ignored - this is always true");
pw.println(" --secondary-dex: compile app secondary dex files");
pw.println(" --split SPLIT: compile only the given split name");
- pw.println(" --compile-layouts: compile layout resources for faster inflation");
pw.println("");
pw.println(" force-dex-opt PACKAGE");
pw.println(" Force immediate execution of dex opt for the given PACKAGE.");
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 8bca4a9fc779..a13c568f87a6 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -466,14 +466,15 @@ final class ResolveIntentHelper {
list = new ArrayList<>(1);
list.add(ri);
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- computer, list, true, intent, resolvedType, filterCallingUid);
+ mPlatformCompat, componentResolver, list, true, intent,
+ resolvedType, filterCallingUid);
}
}
} else {
String pkgName = intent.getPackage();
if (pkgName == null) {
- final List<ResolveInfo> result = componentResolver.queryReceivers(
- computer, intent, resolvedType, flags, filterCallingUid, userId);
+ final List<ResolveInfo> result = componentResolver
+ .queryReceivers(computer, intent, resolvedType, flags, userId);
if (result != null) {
list = result;
}
@@ -481,7 +482,7 @@ final class ResolveIntentHelper {
final AndroidPackage pkg = computer.getPackage(pkgName);
if (pkg != null) {
final List<ResolveInfo> result = componentResolver.queryReceivers(computer,
- intent, resolvedType, flags, pkg.getReceivers(), filterCallingUid, userId);
+ intent, resolvedType, flags, pkg.getReceivers(), userId);
if (result != null) {
list = result;
}
@@ -491,7 +492,8 @@ final class ResolveIntentHelper {
if (originalIntent != null) {
// We also have to ensure all components match the original intent
PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
- computer, list, true, originalIntent, resolvedType, filterCallingUid);
+ mPlatformCompat, componentResolver,
+ list, true, originalIntent, resolvedType, filterCallingUid);
}
return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid,
@@ -575,7 +577,7 @@ final class ResolveIntentHelper {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
- intent, resolvedType, flags, callingUid, userId);
+ intent, resolvedType, flags, userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
@@ -585,7 +587,7 @@ final class ResolveIntentHelper {
final AndroidPackage pkg = computer.getPackage(pkgName);
if (pkg != null) {
final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
- intent, resolvedType, flags, pkg.getProviders(), callingUid, userId);
+ intent, resolvedType, flags, pkg.getProviders(), userId);
if (resolveInfos == null) {
return Collections.emptyList();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c6411ff94b45..d04ce5f3e7f1 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -119,10 +119,9 @@ import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.security.FileIntegrity;
import com.android.server.uri.UriGrantsManagerInternal;
-import libcore.io.IoUtils;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -207,6 +206,10 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
+ @VisibleForTesting
+ static final String FILENAME_USER_PACKAGES_RESERVE_COPY =
+ FILENAME_USER_PACKAGES + ".reservecopy";
+
static final String DIRECTORY_BITMAPS = "bitmaps";
private static final String TAG_ROOT = "root";
@@ -1055,6 +1058,11 @@ public class ShortcutService extends IShortcutService.Stub {
return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
}
+ @VisibleForTesting
+ final File getReserveCopyUserFile(@UserIdInt int userId) {
+ return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES_RESERVE_COPY);
+ }
+
@GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
final File path = getUserFile(userId);
@@ -1062,6 +1070,9 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, "Saving to " + path);
}
+ final File reservePath = getReserveCopyUserFile(userId);
+ reservePath.delete();
+
path.getParentFile().mkdirs();
final AtomicFile file = new AtomicFile(path);
FileOutputStream os = null;
@@ -1079,6 +1090,23 @@ public class ShortcutService extends IShortcutService.Stub {
file.failWrite(os);
}
+ // Store the reserve copy of the file.
+ try (FileInputStream in = new FileInputStream(path);
+ FileOutputStream out = new FileOutputStream(reservePath)) {
+ FileUtils.copy(in, out);
+ FileUtils.sync(out);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write reserve copy: " + path, e);
+ }
+
+ // Protect both primary and reserve copy with fs-verity.
+ try {
+ FileIntegrity.setUpFsVerity(path);
+ FileIntegrity.setUpFsVerity(reservePath);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to verity-protect", e);
+ }
+
getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger);
}
@@ -1117,26 +1145,25 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + path);
}
- final AtomicFile file = new AtomicFile(path);
- final FileInputStream in;
- try {
- in = file.openRead();
+ try (FileInputStream in = new AtomicFile(path).openRead()) {
+ return loadUserInternal(userId, in, /* forBackup= */ false);
} catch (FileNotFoundException e) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Not found " + path);
}
- return null;
- }
- try {
- final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
- return ret;
- } catch (IOException | XmlPullParserException | InvalidFileFormatException e) {
- Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
- return null;
- } finally {
- IoUtils.closeQuietly(in);
+ } catch (Exception e) {
+ final File reservePath = getReserveCopyUserFile(userId);
+ Slog.e(TAG, "Reading from reserve copy: " + reservePath, e);
+ try (FileInputStream in = new AtomicFile(reservePath).openRead()) {
+ return loadUserInternal(userId, in, /* forBackup= */ false);
+ } catch (Exception exceptionReadingReserveFile) {
+ Slog.e(TAG, "Failed to read reserve copy: " + reservePath,
+ exceptionReadingReserveFile);
+ }
+ Slog.e(TAG, "Failed to read file " + path, e);
}
+ return null;
}
private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
@@ -4323,14 +4350,8 @@ public class ShortcutService extends IShortcutService.Stub {
@NonNull ComponentName activity, @UserIdInt int userId) {
final long start = getStatStartTime();
try {
- final ActivityInfo ai;
- try {
- ai = mContext.getPackageManager().getActivityInfoAsUser(activity,
- PackageManager.ComponentInfoFlags.of(PACKAGE_MATCH_FLAGS), userId);
- } catch (NameNotFoundException e) {
- return false;
- }
- return ai.enabled && ai.exported;
+ return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
+ .size() > 0;
} finally {
logDurationStat(Stats.IS_ACTIVITY_ENABLED, start);
}
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 07d36b311aac..18eebe45a759 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Intent;
@@ -39,6 +40,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
@@ -63,6 +65,9 @@ import java.util.Set;
import java.util.function.Predicate;
public final class SuspendPackageHelper {
+
+ private static final String SYSTEM_EXEMPT_FROM_SUSPENSION = "system_exempt_from_suspension";
+
// TODO(b/198166813): remove PMS dependency
private final PackageManagerService mPm;
private final PackageManagerServiceInjector mInjector;
@@ -502,6 +507,10 @@ public final class SuspendPackageHelper {
final String requiredPermissionControllerPackage =
getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
userId);
+ final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
+ final boolean isSystemExemptFlagEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ SYSTEM_EXEMPT_FROM_SUSPENSION, /* defaultValue= */ true);
for (int i = 0; i < packageNames.length; i++) {
canSuspend[i] = false;
final String packageName = packageNames[i];
@@ -558,6 +567,7 @@ public final class SuspendPackageHelper {
PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
if (pkg != null) {
+ final int uid = UserHandle.getUid(userId, packageState.getAppId());
// Cannot suspend SDK libs as they are controlled by SDK manager.
if (pkg.isSdkLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
@@ -574,6 +584,13 @@ public final class SuspendPackageHelper {
+ pkg.getStaticSharedLibraryName());
continue;
}
+ if (isSystemExemptFlagEnabled && appOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
+ == AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set");
+ continue;
+ }
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 977fab16e29b..fac681aaf1c4 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -807,10 +807,10 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
}
- private abstract static class MimeGroupsAwareIntentResolver<F extends ParsedComponent>
- extends IntentResolver<Pair<F, ParsedIntentInfo>, ResolveInfo> {
- private final ArrayMap<String, Pair<F, ParsedIntentInfo>[]> mMimeGroupToFilter =
- new ArrayMap<>();
+ private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<?
+ extends ParsedComponent, ParsedIntentInfo>, R>
+ extends IntentResolver<F, R> {
+ private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
private boolean mIsUpdatingMimeGroup = false;
@NonNull
@@ -822,7 +822,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
// Copy constructor used in creating snapshots
- MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F> orig,
+ MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig,
@NonNull UserManagerService userManager) {
mUserManager = userManager;
copyFrom(orig);
@@ -831,7 +831,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
- public void addFilter(@Nullable PackageDataSnapshot snapshot, Pair<F, ParsedIntentInfo> f) {
+ public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
IntentFilter intentFilter = getIntentFilter(f);
// We assume Computer is available for this class and all subclasses. Because this class
// uses subclass method override to handle logic, the Computer parameter must be in the
@@ -846,7 +846,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
- protected void removeFilterInternal(Pair<F, ParsedIntentInfo> f) {
+ protected void removeFilterInternal(F f) {
IntentFilter intentFilter = getIntentFilter(f);
if (!mIsUpdatingMimeGroup) {
unregister_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
@@ -857,86 +857,6 @@ public class ComponentResolver extends ComponentResolverLocked implements
intentFilter.clearDynamicDataTypes();
}
- @Override
- public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
- String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) {
- if (!mUserManager.exists(userId)) return null;
- long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
- return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid,
- userId, flags);
- }
-
- List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
- String resolvedType, long flags, int callingUid, int userId) {
- if (!mUserManager.exists(userId)) return null;
- return super.queryIntent(computer, intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, callingUid, userId, flags);
- }
-
- List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
- String resolvedType, long flags, List<F> packageComponents,
- int callingUid, int userId) {
- if (!mUserManager.exists(userId)) {
- return null;
- }
- if (packageComponents == null) {
- return Collections.emptyList();
- }
- final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
- final int componentsSize = packageComponents.size();
- ArrayList<Pair<F, ParsedIntentInfo>[]> listCut = new ArrayList<>(componentsSize);
-
- List<ParsedIntentInfo> intentFilters;
- for (int i = 0; i < componentsSize; ++i) {
- F component = packageComponents.get(i);
- intentFilters = component.getIntents();
- if (!intentFilters.isEmpty()) {
- Pair<F, ParsedIntentInfo>[] array = newArray(intentFilters.size());
- for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
- array[arrayIndex] = Pair.create(component, intentFilters.get(arrayIndex));
- }
- listCut.add(array);
- }
- }
- return super.queryIntentFromList(computer, intent, resolvedType,
- defaultOnly, listCut, callingUid, userId, flags);
- }
-
- @Override
- protected boolean isPackageForFilter(String packageName, Pair<F, ParsedIntentInfo> info) {
- return packageName.equals(info.first.getPackageName());
- }
-
- @Override
- protected void sortResults(List<ResolveInfo> results) {
- results.sort(RESOLVE_PRIORITY_SORTER);
- }
-
- @Override
- protected void filterResults(@NonNull Computer computer, @NonNull Intent intent,
- List<ResolveInfo> results) {
- if (intent.getAction() != null) return;
- // When the resolved component is targeting U+, block null action intents
- for (int i = results.size() - 1; i >= 0; --i) {
- if (computer.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS,
- results.get(i).getComponentInfo().applicationInfo)) {
- results.remove(i);
- }
- }
- }
-
- @Override
- protected Pair<F, ParsedIntentInfo>[] newArray(int size) {
- //noinspection unchecked
- return (Pair<F, ParsedIntentInfo>[]) new Pair<?, ?>[size];
- }
-
- @Override
- protected IntentFilter getIntentFilter(
- @NonNull Pair<F, ParsedIntentInfo> input) {
- return input.second.getIntentFilter();
- }
-
/**
* Updates MIME group by applying changes to all IntentFilters
* that contain the group and repopulating m*ToFilter maps accordingly
@@ -947,12 +867,12 @@ public class ComponentResolver extends ComponentResolverLocked implements
*/
public boolean updateMimeGroup(@NonNull Computer computer, String packageName,
String mimeGroup) {
- Pair<F, ParsedIntentInfo>[] filters = mMimeGroupToFilter.get(mimeGroup);
+ F[] filters = mMimeGroupToFilter.get(mimeGroup);
int n = filters != null ? filters.length : 0;
mIsUpdatingMimeGroup = true;
boolean hasChanges = false;
- Pair<F, ParsedIntentInfo> filter;
+ F filter;
for (int i = 0; i < n && (filter = filters[i]) != null; i++) {
if (isPackageForFilter(packageName, filter)) {
hasChanges |= updateFilter(computer, filter);
@@ -962,7 +882,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
return hasChanges;
}
- private boolean updateFilter(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) {
+ private boolean updateFilter(@NonNull Computer computer, F f) {
IntentFilter filter = getIntentFilter(f);
List<String> oldTypes = filter.dataTypes();
removeFilter(f);
@@ -987,7 +907,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
return first.equals(second);
}
- private void applyMimeGroups(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) {
+ private void applyMimeGroups(@NonNull Computer computer, F f) {
IntentFilter filter = getIntentFilter(f);
for (int i = filter.countMimeGroups() - 1; i >= 0; i--) {
@@ -1011,8 +931,8 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
- protected boolean isFilterStopped(@NonNull Computer computer,
- Pair<F, ParsedIntentInfo> filter, @UserIdInt int userId) {
+ protected boolean isFilterStopped(@NonNull Computer computer, F filter,
+ @UserIdInt int userId) {
if (!mUserManager.exists(userId)) {
return true;
}
@@ -1028,7 +948,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
public static class ActivityIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedActivity> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
@NonNull
private UserNeedsBadgingCache mUserNeedsBadging;
@@ -1049,6 +969,53 @@ public class ComponentResolver extends ComponentResolverLocked implements
mUserNeedsBadging = userNeedsBadgingCache;
}
+ @Override
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
+ }
+
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+ }
+
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedActivity> packageActivities,
+ int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ if (packageActivities == null) {
+ return Collections.emptyList();
+ }
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final int activitiesSize = packageActivities.size();
+ ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(activitiesSize);
+
+ List<ParsedIntentInfo> intentFilters;
+ for (int i = 0; i < activitiesSize; ++i) {
+ ParsedActivity activity = packageActivities.get(i);
+ intentFilters = activity.getIntents();
+ if (!intentFilters.isEmpty()) {
+ Pair<ParsedActivity, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(activity, intentFilters.get(arrayIndex));
+ }
+ listCut.add(array);
+ }
+ }
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
+ }
+
protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type,
List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
mActivities.put(a.getComponentName(), a);
@@ -1105,6 +1072,18 @@ public class ComponentResolver extends ComponentResolverLocked implements
return true;
}
+ @Override
+ protected Pair<ParsedActivity, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedActivity, ParsedIntentInfo>[]) new Pair<?, ?>[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName,
+ Pair<ParsedActivity, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
+ }
+
private void log(String reason, ParsedIntentInfo info, int match,
int userId) {
Slog.w(TAG, reason
@@ -1220,6 +1199,11 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
+ protected void sortResults(List<ResolveInfo> results) {
+ results.sort(RESOLVE_PRIORITY_SORTER);
+ }
+
+ @Override
protected void dumpFilter(PrintWriter out, String prefix,
Pair<ParsedActivity, ParsedIntentInfo> pair) {
ParsedActivity activity = pair.first;
@@ -1253,6 +1237,12 @@ public class ComponentResolver extends ComponentResolverLocked implements
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedActivity, ParsedIntentInfo> input) {
+ return input.second.getIntentFilter();
+ }
+
protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
return pkg.getActivities();
}
@@ -1288,7 +1278,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
public static final class ProviderIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedProvider> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
ProviderIntentResolver(@NonNull UserManagerService userManager) {
super(userManager);
@@ -1301,6 +1291,57 @@ public class ComponentResolver extends ComponentResolverLocked implements
mProviders.putAll(orig.mProviders);
}
+ @Override
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
+ }
+
+ @Nullable
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+ }
+
+ @Nullable
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedProvider> packageProviders,
+ int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ if (packageProviders == null) {
+ return Collections.emptyList();
+ }
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final int providersSize = packageProviders.size();
+ ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(providersSize);
+
+ List<ParsedIntentInfo> intentFilters;
+ for (int i = 0; i < providersSize; ++i) {
+ ParsedProvider provider = packageProviders.get(i);
+ intentFilters = provider.getIntents();
+ if (!intentFilters.isEmpty()) {
+ Pair<ParsedProvider, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(provider, intentFilters.get(arrayIndex));
+ }
+ listCut.add(array);
+ }
+ }
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
+ }
+
void addProvider(@NonNull Computer computer, ParsedProvider p) {
if (mProviders.containsKey(p.getComponentName())) {
Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
@@ -1361,6 +1402,18 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
+ protected Pair<ParsedProvider, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedProvider, ParsedIntentInfo>[]) new Pair<?, ?>[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName,
+ Pair<ParsedProvider, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
+ }
+
+ @Override
protected ResolveInfo newResult(@NonNull Computer computer,
Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId,
long customFlags) {
@@ -1426,6 +1479,11 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
+ protected void sortResults(List<ResolveInfo> results) {
+ results.sort(RESOLVE_PRIORITY_SORTER);
+ }
+
+ @Override
protected void dumpFilter(PrintWriter out, String prefix,
Pair<ParsedProvider, ParsedIntentInfo> pair) {
ParsedProvider provider = pair.first;
@@ -1460,11 +1518,17 @@ public class ComponentResolver extends ComponentResolverLocked implements
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedProvider, ParsedIntentInfo> input) {
+ return input.second.getIntentFilter();
+ }
+
final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
}
public static final class ServiceIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedService> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
ServiceIntentResolver(@NonNull UserManagerService userManager) {
super(userManager);
@@ -1477,6 +1541,50 @@ public class ComponentResolver extends ComponentResolverLocked implements
mServices.putAll(orig.mServices);
}
+ @Override
+ public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+ String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+ if (!mUserManager.exists(userId)) {
+ return null;
+ }
+ long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
+ }
+
+ List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ return super.queryIntent(computer, intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+ }
+
+ List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+ String resolvedType, long flags, List<ParsedService> packageServices, int userId) {
+ if (!mUserManager.exists(userId)) return null;
+ if (packageServices == null) {
+ return Collections.emptyList();
+ }
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final int servicesSize = packageServices.size();
+ ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(servicesSize);
+
+ List<ParsedIntentInfo> intentFilters;
+ for (int i = 0; i < servicesSize; ++i) {
+ ParsedService service = packageServices.get(i);
+ intentFilters = service.getIntents();
+ if (intentFilters.size() > 0) {
+ Pair<ParsedService, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(service, intentFilters.get(arrayIndex));
+ }
+ listCut.add(array);
+ }
+ }
+ return super.queryIntentFromList(computer, intent, resolvedType,
+ defaultOnly, listCut, userId, flags);
+ }
+
void addService(@NonNull Computer computer, ParsedService s) {
mServices.put(s.getComponentName(), s);
if (DEBUG_SHOW_INFO) {
@@ -1532,6 +1640,18 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
+ protected Pair<ParsedService, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedService, ParsedIntentInfo>[]) new Pair<?, ?>[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName,
+ Pair<ParsedService, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
+ }
+
+ @Override
protected ResolveInfo newResult(@NonNull Computer computer,
Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId,
long customFlags) {
@@ -1590,6 +1710,11 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
+ protected void sortResults(List<ResolveInfo> results) {
+ results.sort(RESOLVE_PRIORITY_SORTER);
+ }
+
+ @Override
protected void dumpFilter(PrintWriter out, String prefix,
Pair<ParsedService, ParsedIntentInfo> pair) {
ParsedService service = pair.first;
@@ -1627,6 +1752,12 @@ public class ComponentResolver extends ComponentResolverLocked implements
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedService, ParsedIntentInfo> input) {
+ return input.second.getIntentFilter();
+ }
+
// Keys are String (activity class name), values are Activity.
final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
}
@@ -1690,8 +1821,7 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
- protected void filterResults(@NonNull Computer computer,
- @NonNull Intent intent, List<AuxiliaryResolveInfo.AuxiliaryFilter> results) {
+ protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) {
// only do work if ordering is enabled [most of the time it won't be]
if (mOrderResult.size() == 0) {
return;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
index 7f886600121c..b8e4c8d2a51f 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -55,12 +55,12 @@ public interface ComponentResolverApi {
@Nullable
List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId);
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
@Nullable
List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
- int callingUid, @UserIdInt int userId);
+ @UserIdInt int userId);
@Nullable
ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags,
@@ -68,12 +68,12 @@ public interface ComponentResolverApi {
@Nullable
List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId);
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
@Nullable
List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
- int callingUid, @UserIdInt int userId);
+ @UserIdInt int userId);
@Nullable
List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName,
@@ -81,21 +81,21 @@ public interface ComponentResolverApi {
@Nullable
List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId);
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
@Nullable
List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
- int callingUid, @UserIdInt int userId);
+ @UserIdInt int userId);
@Nullable
List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId);
+ @Nullable String resolvedType, long flags, @UserIdInt int userId);
@Nullable
List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
- int callingUid, @UserIdInt int userId);
+ @UserIdInt int userId);
void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
@NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId);
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 689992444601..9115775acd05 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -126,17 +126,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com
@Nullable
@Override
public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, int userId) {
- return mActivities.queryIntent(computer, intent, resolvedType, flags, callingUid, userId);
+ @Nullable String resolvedType, long flags, int userId) {
+ return mActivities.queryIntent(computer, intent, resolvedType, flags, userId);
}
@Nullable
@Override
public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
- int callingUid, int userId) {
+ int userId) {
return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities,
- callingUid, userId);
+ userId);
}
@Nullable
@@ -168,17 +168,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com
@Nullable
@Override
public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, int userId) {
- return mProviders.queryIntent(computer, intent, resolvedType, flags, callingUid, userId);
+ @Nullable String resolvedType, long flags, int userId) {
+ return mProviders.queryIntent(computer, intent, resolvedType, flags, userId);
}
@Nullable
@Override
public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers,
- callingUid, userId);
+ userId);
}
@Nullable
@@ -241,33 +241,33 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com
@Nullable
@Override
public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, int userId) {
- return mReceivers.queryIntent(computer, intent, resolvedType, flags, callingUid, userId);
+ @Nullable String resolvedType, long flags, int userId) {
+ return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId);
}
@Nullable
@Override
public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers,
- callingUid, userId);
+ userId);
}
@Nullable
@Override
public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) {
- return mServices.queryIntent(computer, intent, resolvedType, flags, callingUid, userId);
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+ return mServices.queryIntent(computer, intent, resolvedType, flags, userId);
}
@Nullable
@Override
public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services,
- callingUid, userId);
+ userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
index 5bfb135c7d75..0c84f4c53dfe 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
@@ -92,9 +92,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Nullable
@Override
public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) {
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryActivities(computer, intent, resolvedType, flags, callingUid, userId);
+ return super.queryActivities(computer, intent, resolvedType, flags, userId);
}
}
@@ -102,10 +102,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Override
public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryActivities(computer, intent, resolvedType, flags, activities,
- callingUid, userId);
+ return super.queryActivities(computer, intent, resolvedType, flags, activities, userId);
}
}
@@ -121,9 +120,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Nullable
@Override
public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) {
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryProviders(computer, intent, resolvedType, flags, callingUid, userId);
+ return super.queryProviders(computer, intent, resolvedType, flags, userId);
}
}
@@ -131,10 +130,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Override
public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryProviders(computer, intent, resolvedType, flags, providers,
- callingUid, userId);
+ return super.queryProviders(computer, intent, resolvedType, flags, providers, userId);
}
}
@@ -151,9 +149,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Nullable
@Override
public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) {
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryReceivers(computer, intent, resolvedType, flags, callingUid, userId);
+ return super.queryReceivers(computer, intent, resolvedType, flags, userId);
}
}
@@ -161,19 +159,18 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Override
public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryReceivers(computer, intent, resolvedType, flags, receivers,
- callingUid, userId);
+ return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId);
}
}
@Nullable
@Override
public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
- @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) {
+ @Nullable String resolvedType, long flags, @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryServices(computer, intent, resolvedType, flags, callingUid, userId);
+ return super.queryServices(computer, intent, resolvedType, flags, userId);
}
}
@@ -181,10 +178,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase {
@Override
public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
@Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
- int callingUid, @UserIdInt int userId) {
+ @UserIdInt int userId) {
synchronized (mLock) {
- return super.queryServices(computer, intent, resolvedType, flags, services, callingUid,
- userId);
+ return super.queryServices(computer, intent, resolvedType, flags, services, userId);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a24f129cc575..37877cf8264e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -89,6 +89,7 @@ import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
@@ -160,6 +161,7 @@ import android.service.dreams.IDreamManager;
import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableBoolean;
@@ -398,7 +400,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
InputManagerInternal mInputManagerInternal;
- InputMethodManagerInternal mInputMethodManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
@@ -659,6 +660,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int MSG_HANDLE_ALL_APPS = 22;
private static final int MSG_LAUNCH_ASSIST = 23;
private static final int MSG_RINGER_TOGGLE_CHORD = 24;
+ private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25;
private class PolicyHandler extends Handler {
@Override
@@ -729,6 +731,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case MSG_SCREENSHOT_CHORD:
handleScreenShot(msg.arg1);
break;
+ case MSG_SWITCH_KEYBOARD_LAYOUT:
+ handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+ break;
}
}
}
@@ -1025,14 +1030,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
if (mDismissImeOnBackKeyPressed) {
- if (mInputMethodManagerInternal == null) {
- mInputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- }
- if (mInputMethodManagerInternal != null) {
- mInputMethodManagerInternal.hideCurrentInputMethod(
+ InputMethodManagerInternal.get().hideCurrentInputMethod(
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
- }
} else {
shortPressPowerGoHome();
}
@@ -2978,15 +2977,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case KeyEvent.KEYCODE_N:
if (down && event.isMetaPressed()) {
- IStatusBarService service = getStatusBarService();
- if (service != null) {
- try {
- service.expandNotificationsPanel();
- } catch (RemoteException e) {
- // do nothing.
- }
- return key_consumed;
- }
+ toggleNotificationPanel();
+ return key_consumed;
}
break;
case KeyEvent.KEYCODE_S:
@@ -3177,7 +3169,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (down && repeatCount == 0) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ sendSwitchKeyboardLayout(event, direction);
return key_consumed;
}
break;
@@ -3427,7 +3419,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
KeyEvent.META_CTRL_ON)) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ sendSwitchKeyboardLayout(event, direction);
return true;
}
}
@@ -3453,6 +3445,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return false;
}
+ private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) {
+ mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(),
+ direction).sendToTarget();
+ }
+
+ private void handleSwitchKeyboardLayout(int deviceId, int direction) {
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
+ InputMethodManagerInternal.get().switchKeyboardLayout(direction);
+ } else {
+ mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction);
+ }
+ }
+
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
diff --git a/services/core/java/com/android/server/rollback/OWNERS b/services/core/java/com/android/server/rollback/OWNERS
index 7feb85f929cd..daa02111f71f 100644
--- a/services/core/java/com/android/server/rollback/OWNERS
+++ b/services/core/java/com/android/server/rollback/OWNERS
@@ -1 +1,3 @@
-olilan@google.com
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 41c2fbfd3314..6c32ec2e8df8 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,8 @@
package com.android.server.sensors;
import android.annotation.NonNull;
+import android.hardware.SensorDirectChannel;
+import android.os.ParcelFileDescriptor;
import java.util.concurrent.Executor;
@@ -58,7 +60,7 @@ public abstract class SensorManagerInternal {
* @return The sensor handle.
*/
public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name,
- @NonNull String vendor, @NonNull RuntimeSensorCallback callback);
+ @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback);
/**
* Unregisters the sensor with the given handle from the framework.
@@ -98,9 +100,31 @@ public abstract class SensorManagerInternal {
public interface RuntimeSensorCallback {
/**
* Invoked when the listeners of the runtime sensor have changed.
- * Returns an error code if the invocation was unsuccessful, zero otherwise.
+ * Returns zero on success, negative error code otherwise.
*/
int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros,
int batchReportLatencyMicros);
+
+ /**
+ * Invoked when a direct sensor channel has been created.
+ * Wraps the file descriptor in a {@link android.os.SharedMemory} object and passes it to
+ * the client process.
+ * Returns a positive identifier of the channel on success, negative error code otherwise.
+ */
+ int onDirectChannelCreated(ParcelFileDescriptor fd);
+
+ /**
+ * Invoked when a direct sensor channel has been destroyed.
+ */
+ void onDirectChannelDestroyed(int channelHandle);
+
+ /**
+ * Invoked when a direct sensor channel has been configured for a sensor.
+ * If the invocation is unsuccessful, a negative error code is returned.
+ * On success, the return value is zero if the rate level is {@code RATE_STOP}, and a
+ * positive report token otherwise.
+ */
+ int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ @SensorDirectChannel.RateLevel int rateLevel);
}
}
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 979065950dc4..1baa0a6d79a1 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -56,7 +56,8 @@ public class SensorService extends SystemService {
private static native void unregisterProximityActiveListenerNative(long ptr);
private static native int registerRuntimeSensorNative(long ptr, int deviceId, int type,
- String name, String vendor, SensorManagerInternal.RuntimeSensorCallback callback);
+ String name, String vendor, int flags,
+ SensorManagerInternal.RuntimeSensorCallback callback);
private static native void unregisterRuntimeSensorNative(long ptr, int handle);
private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
long timestampNanos, float[] values);
@@ -95,9 +96,9 @@ public class SensorService extends SystemService {
class LocalService extends SensorManagerInternal {
@Override
public int createRuntimeSensor(int deviceId, int type, @NonNull String name,
- @NonNull String vendor, @NonNull RuntimeSensorCallback callback) {
+ @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback) {
synchronized (mLock) {
- int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor,
+ int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, flags,
callback);
mRuntimeSensorHandles.add(handle);
return handle;
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
index ed3248ecc165..de631bb53377 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
@@ -181,24 +181,6 @@ public class NetworkTimeUpdateService extends Binder {
}
/**
- * Clears the cached NTP time. For use during tests to simulate when no NTP time is available.
- *
- * <p>This operation takes place in the calling thread rather than the service's handler thread.
- */
- @RequiresPermission(android.Manifest.permission.SET_TIME)
- void clearTimeForTests() {
- mContext.enforceCallingPermission(
- android.Manifest.permission.SET_TIME, "clear latest network time");
-
- final long token = Binder.clearCallingIdentity();
- try {
- mNtpTrustedTime.clearCachedTimeResult();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
* Forces the service to refresh the NTP time.
*
* <p>This operation takes place in the calling thread rather than the service's handler thread.
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
index afc0bdd30f29..cfc95df1ef15 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
@@ -37,11 +37,6 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand {
private static final String SHELL_COMMAND_SERVICE_NAME = "network_time_update_service";
/**
- * A shell command that clears the time signal received from the network.
- */
- private static final String SHELL_COMMAND_CLEAR_TIME = "clear_time";
-
- /**
* A shell command that forces the time signal to be refreshed from the network.
*/
private static final String SHELL_COMMAND_FORCE_REFRESH = "force_refresh";
@@ -73,8 +68,6 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand {
}
switch (cmd) {
- case SHELL_COMMAND_CLEAR_TIME:
- return runClearTime();
case SHELL_COMMAND_FORCE_REFRESH:
return runForceRefresh();
case SHELL_COMMAND_SET_SERVER_CONFIG:
@@ -87,11 +80,6 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand {
}
}
- private int runClearTime() {
- mNetworkTimeUpdateService.clearTimeForTests();
- return 0;
- }
-
private int runForceRefresh() {
boolean success = mNetworkTimeUpdateService.forceRefreshForTests();
getOutPrintWriter().println(success);
@@ -147,8 +135,6 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand {
pw.printf("Network Time Update Service (%s) commands:\n", SHELL_COMMAND_SERVICE_NAME);
pw.printf(" help\n");
pw.printf(" Print this help text.\n");
- pw.printf(" %s\n", SHELL_COMMAND_CLEAR_TIME);
- pw.printf(" Clears the latest time.\n");
pw.printf(" %s\n", SHELL_COMMAND_FORCE_REFRESH);
pw.printf(" Refreshes the latest time. Prints whether it was successful.\n");
pw.printf(" %s\n", SHELL_COMMAND_SET_SERVER_CONFIG);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0da967a3bc00..22f096b11f18 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -37,6 +37,7 @@ import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.NtpTrustedTime;
@@ -53,6 +54,7 @@ import com.android.server.timezonedetector.CurrentUserIdentityInjector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.InetSocketAddress;
import java.time.DateTimeException;
import java.util.Objects;
@@ -377,7 +379,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
*
* <p>This operation takes place in the calling thread.
*/
- void clearNetworkTime() {
+ void clearLatestNetworkTime() {
enforceSuggestNetworkTimePermission();
final long token = Binder.clearCallingIdentity();
@@ -390,12 +392,29 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
@Override
public UnixEpochTime latestNetworkTime() {
- NetworkTimeSuggestion suggestion = getLatestNetworkSuggestion();
- if (suggestion != null) {
- return suggestion.getUnixEpochTime();
+ NetworkTimeSuggestion latestNetworkTime;
+ // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+ // NtpTrustedTime result in a suggestion being made to the time detector.
+ // mNtpTrustedTime can be removed once this happens.
+ if (TimeDetectorNetworkTimeHelper.isInUse()) {
+ // The new implementation.
+ latestNetworkTime = mTimeDetectorStrategy.getLatestNetworkSuggestion();
} else {
+ // The old implementation.
+ NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
+ if (ntpResult != null) {
+ latestNetworkTime = new NetworkTimeSuggestion(
+ new UnixEpochTime(
+ ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis()),
+ ntpResult.getUncertaintyMillis());
+ } else {
+ latestNetworkTime = null;
+ }
+ }
+ if (latestNetworkTime == null) {
throw new ParcelableException(new DateTimeException("Missing network time fix"));
}
+ return latestNetworkTime.getUnixEpochTime();
}
/**
@@ -403,23 +422,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
*/
@Nullable
NetworkTimeSuggestion getLatestNetworkSuggestion() {
- // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
- // be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
- // detector. mNtpTrustedTime can be removed once this happens.
- if (TimeDetectorNetworkTimeHelper.isInUse()) {
- // The new implementation.
- return mTimeDetectorStrategy.getLatestNetworkSuggestion();
- } else {
- // The old implementation.
- NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
- if (ntpResult != null) {
- UnixEpochTime unixEpochTime = new UnixEpochTime(
- ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
- return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
- } else {
- return null;
- }
- }
+ return mTimeDetectorStrategy.getLatestNetworkSuggestion();
}
/**
@@ -440,6 +443,57 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal));
}
+ /**
+ * Sets the network time for testing {@link SystemClock#currentNetworkTimeClock()}.
+ *
+ * <p>This operation takes place in the calling thread.
+ */
+ void setNetworkTimeForSystemClockForTests(
+ @NonNull UnixEpochTime unixEpochTime, int uncertaintyMillis) {
+ enforceSuggestNetworkTimePermission();
+
+ // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+ // NtpTrustedTime result in a suggestion being made to the time detector.
+ // mNtpTrustedTime can be removed once this happens.
+ if (TimeDetectorNetworkTimeHelper.isInUse()) {
+ NetworkTimeSuggestion suggestion =
+ new NetworkTimeSuggestion(unixEpochTime, uncertaintyMillis);
+ suggestion.addDebugInfo("Injected for tests");
+ mTimeDetectorStrategy.suggestNetworkTime(suggestion);
+ } else {
+ NtpTrustedTime.TimeResult timeResult = new NtpTrustedTime.TimeResult(
+ unixEpochTime.getUnixEpochTimeMillis(),
+ unixEpochTime.getElapsedRealtimeMillis(),
+ uncertaintyMillis,
+ InetSocketAddress.createUnresolved("time.set.for.tests", 123));
+ mNtpTrustedTime.setCachedTimeResult(timeResult);
+ }
+ }
+
+ /**
+ * Clears the network time for testing {@link SystemClock#currentNetworkTimeClock()}.
+ *
+ * <p>This operation takes place in the calling thread.
+ */
+ void clearNetworkTimeForSystemClockForTests() {
+ enforceSuggestNetworkTimePermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+ // NtpTrustedTime result in a suggestion being made to the time detector.
+ // mNtpTrustedTime can be removed once this happens.
+ if (TimeDetectorNetworkTimeHelper.isInUse()) {
+ // Clear the latest network suggestion. Done in all c
+ mTimeDetectorStrategy.clearLatestNetworkSuggestion();
+ } else {
+ mNtpTrustedTime.clearCachedTimeResult();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
index cce570986168..fe0127fc11f2 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
@@ -16,12 +16,14 @@
package com.android.server.timedetector;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CLEAR_NETWORK_TIME;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CONFIRM_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_NETWORK_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_TIME_STATE;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_TIME_STATE;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_EXTERNAL_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_GNSS_TIME;
@@ -73,9 +75,9 @@ class TimeDetectorShellCommand extends ShellCommand {
case SHELL_COMMAND_SUGGEST_NETWORK_TIME:
return runSuggestNetworkTime();
case SHELL_COMMAND_GET_NETWORK_TIME:
- return runGetNetworkTime();
+ return runGetLatestNetworkTime();
case SHELL_COMMAND_CLEAR_NETWORK_TIME:
- return runClearNetworkTime();
+ return runClearLatestNetworkTime();
case SHELL_COMMAND_SUGGEST_GNSS_TIME:
return runSuggestGnssTime();
case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME:
@@ -86,6 +88,10 @@ class TimeDetectorShellCommand extends ShellCommand {
return runSetTimeState();
case SHELL_COMMAND_CONFIRM_TIME:
return runConfirmTime();
+ case SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME:
+ return runClearSystemClockNetworkTime();
+ case SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME:
+ return runSetSystemClockNetworkTime();
default: {
return handleDefaultCommands(cmd);
}
@@ -128,15 +134,15 @@ class TimeDetectorShellCommand extends ShellCommand {
mInterface::suggestNetworkTime);
}
- private int runGetNetworkTime() {
+ private int runGetLatestNetworkTime() {
NetworkTimeSuggestion networkTimeSuggestion = mInterface.getLatestNetworkSuggestion();
final PrintWriter pw = getOutPrintWriter();
pw.println(networkTimeSuggestion);
return 0;
}
- private int runClearNetworkTime() {
- mInterface.clearNetworkTime();
+ private int runClearLatestNetworkTime() {
+ mInterface.clearLatestNetworkTime();
return 0;
}
@@ -187,6 +193,20 @@ class TimeDetectorShellCommand extends ShellCommand {
return 0;
}
+ private int runClearSystemClockNetworkTime() {
+ mInterface.clearNetworkTimeForSystemClockForTests();
+ return 0;
+ }
+
+ private int runSetSystemClockNetworkTime() {
+ NetworkTimeSuggestion networkTimeSuggestion =
+ NetworkTimeSuggestion.parseCommandLineArg(this);
+ mInterface.setNetworkTimeForSystemClockForTests(
+ networkTimeSuggestion.getUnixEpochTime(),
+ networkTimeSuggestion.getUncertaintyMillis());
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -218,6 +238,16 @@ class TimeDetectorShellCommand extends ShellCommand {
pw.printf(" Prints the network time information held by the detector.\n");
pw.printf(" %s\n", SHELL_COMMAND_CLEAR_NETWORK_TIME);
pw.printf(" Clears the network time information held by the detector.\n");
+ // TODO(b/222295093) Remove these "system_clock" commands when
+ // SystemClock.currentNetworkTimeClock() is guaranteed to use the latest network
+ // suggestion. Then, commands above can be used instead.
+ pw.printf(" %s <network suggestion opts>\n",
+ SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME);
+ pw.printf(" Sets the network time information used for"
+ + " SystemClock.currentNetworkTimeClock().\n");
+ pw.printf(" %s\n", SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME);
+ pw.printf(" Clears the network time information used for"
+ + " SystemClock.currentNetworkTimeClock().\n");
pw.println();
ManualTimeSuggestion.printCommandLineOpts(pw);
pw.println();
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 91c4a2ff03e0..8a4fc0db0264 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -42,6 +42,9 @@ class ActivitySecurityModelFeatureFlags {
// TODO(b/230590090): Replace with public documentation once ready
static final String DOC_LINK = "go/android-asm";
+ /** Used to determine which version of the ASM logic was used in logs while we iterate */
+ static final int ASM_VERSION = 5;
+
private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 32dac49102bd..e99046041056 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -60,7 +60,6 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.wm.ActivityRecord.State.FINISHING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -75,6 +74,7 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_UID;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT;
@@ -83,6 +83,7 @@ import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
+import static com.android.server.wm.BackgroundActivityStartController.balCodeToString;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
@@ -150,6 +151,9 @@ import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -1967,8 +1971,7 @@ class ActivityStarter {
// ASM rules have failed. Log why
ActivityRecord targetTopActivity = targetTask == null ? null
- : targetTask.getActivity(ar ->
- !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
+ : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());
int action = newTask || mSourceRecord == null
? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK
@@ -1999,7 +2002,7 @@ class ActivityStarter {
/* action */
action,
/* version */
- 4,
+ ActivitySecurityModelFeatureFlags.ASM_VERSION,
/* multi_window - we have our source not in the target task, but both are visible */
targetTask != null && mSourceRecord != null
&& !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible(),
@@ -2011,22 +2014,26 @@ class ActivityStarter {
.shouldRestrictActivitySwitch(mCallingUid)
&& shouldBlockActivityStart;
+ String launchedFromPackageName = r.launchedFromPackage;
if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
+ String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK
+ + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
+ + getApplicationLabel(mService.mContext.getPackageManager(),
+ launchedFromPackageName);
UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- "Activity start from " + r.launchedFromPackage
- + (blockActivityStartAndFeatureEnabled ? " " : " would be ")
- + "blocked by " + ActivitySecurityModelFeatureFlags.DOC_LINK,
- Toast.LENGTH_SHORT).show());
- }
+ toastText, Toast.LENGTH_LONG).show());
+ logDebugInfoForActivitySecurity("Launch", r, targetTask, targetTopActivity,
+ blockActivityStartAndFeatureEnabled, /* taskToFront */ taskToFront);
+ }
if (blockActivityStartAndFeatureEnabled) {
- Slog.e(TAG, "Abort Launching r: " + r
+ Slog.e(TAG, "[ASM] Abort Launching r: " + r
+ " as source: "
- + (mSourceRecord != null ? mSourceRecord : r.launchedFromPackage)
+ + (mSourceRecord != null ? mSourceRecord : launchedFromPackageName)
+ " is in background. New task: " + newTask
+ ". Top activity: " + targetTopActivity
- + ". BAL Code: " + mBalCode);
+ + ". BAL Code: " + balCodeToString(mBalCode));
return false;
}
@@ -2034,6 +2041,71 @@ class ActivityStarter {
return true;
}
+ /** Only called when an activity launch may be blocked, which should happen very rarely */
+ private void logDebugInfoForActivitySecurity(String action, ActivityRecord r, Task targetTask,
+ ActivityRecord targetTopActivity, boolean blockActivityStartAndFeatureEnabled,
+ boolean taskToFront) {
+ final String prefix = "[ASM] ";
+ Function<ActivityRecord, String> recordToString = (ar) -> {
+ if (ar == null) {
+ return null;
+ }
+ return (ar == mSourceRecord ? " [source]=> "
+ : ar == targetTopActivity ? " [ top ]=> "
+ : ar == r ? " [target]=> "
+ : " => ")
+ + ar
+ + " :: visible=" + ar.isVisible()
+ + ", finishing=" + ar.isFinishing()
+ + ", alwaysOnTop=" + ar.isAlwaysOnTop()
+ + ", taskFragment=" + ar.getTaskFragment();
+ };
+
+ StringJoiner joiner = new StringJoiner("\n");
+ joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
+ joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
+ joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
+
+ boolean targetTaskMatchesSourceTask = targetTask != null
+ && mSourceRecord != null && mSourceRecord.getTask() == targetTask;
+
+ if (mSourceRecord == null) {
+ joiner.add(prefix + "Source Package: " + r.launchedFromPackage);
+ String realCallingPackage = mService.mContext.getPackageManager().getNameForUid(
+ mRealCallingUid);
+ joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage);
+ } else {
+ joiner.add(prefix + "Source Record: " + recordToString.apply(mSourceRecord));
+ if (targetTaskMatchesSourceTask) {
+ joiner.add(prefix + "Source/Target Task: " + mSourceRecord.getTask());
+ joiner.add(prefix + "Source/Target Task Stack: ");
+ } else {
+ joiner.add(prefix + "Source Task: " + mSourceRecord.getTask());
+ joiner.add(prefix + "Source Task Stack: ");
+ }
+ mSourceRecord.getTask().forAllActivities((Consumer<ActivityRecord>)
+ ar -> joiner.add(prefix + recordToString.apply(ar)));
+ }
+
+ joiner.add(prefix + "Target Task Top: " + recordToString.apply(targetTopActivity));
+ if (!targetTaskMatchesSourceTask) {
+ joiner.add(prefix + "Target Task: " + targetTask);
+ if (targetTask != null) {
+ joiner.add(prefix + "Target Task Stack: ");
+ targetTask.forAllActivities((Consumer<ActivityRecord>)
+ ar -> joiner.add(prefix + recordToString.apply(ar)));
+ }
+ }
+
+ joiner.add(prefix + "Target Record: " + recordToString.apply(r));
+ joiner.add(prefix + "Intent: " + mIntent);
+ joiner.add(prefix + "TaskToFront: " + taskToFront);
+ joiner.add(prefix + "BalCode: " + balCodeToString(mBalCode));
+
+ joiner.add(prefix + "------ Activity Security " + action + " Debug Logging End ------");
+ Slog.i(TAG, joiner.toString());
+ }
+
/**
* Returns whether embedding of {@code starting} is allowed.
*
@@ -2165,8 +2237,8 @@ class ActivityStarter {
return;
}
- Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing && (ar.isUid(
- startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid));
+ Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing
+ && (ar.isUid(startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid));
// Return early if we know for sure we won't need to clear any activities by just checking
// the top activity.
@@ -2202,7 +2274,10 @@ class ActivityStarter {
? "Top activities cleared by "
: "Top activities would be cleared by ")
+ ActivitySecurityModelFeatureFlags.DOC_LINK,
- Toast.LENGTH_SHORT).show());
+ Toast.LENGTH_LONG).show());
+
+ logDebugInfoForActivitySecurity("Clear Top", mStartActivity, targetTask, targetTaskTop,
+ shouldBlockActivityStart, /* taskToFront */ true);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index ef47b6e5d70a..e463358a3cf5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -51,7 +51,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.wm.ActivityRecord.State.FINISHING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
@@ -103,6 +102,7 @@ import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -1236,7 +1236,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
int getDeviceIdForDisplayId(int displayId) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
- return VirtualDeviceManager.DEVICE_ID_DEFAULT;
+ return Context.DEVICE_ID_DEFAULT;
}
if (mVirtualDeviceManager == null) {
mVirtualDeviceManager =
@@ -1636,50 +1636,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Prevent recursion.
return;
}
- boolean shouldBlockActivitySwitchIfFeatureEnabled = false;
- boolean wouldBlockActivitySwitchIgnoringFlags = false;
- // We may have already checked that the callingUid has additional clearTask privileges, and
- // cleared the calling identify. If so, we infer we do not need further restrictions here.
- // TODO(b/263368846) Move to live with the rest of the ASM logic.
- if (callingUid != SYSTEM_UID) {
- Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task,
- callingUid,
- null);
- shouldBlockActivitySwitchIfFeatureEnabled = !pair.first;
- wouldBlockActivitySwitchIgnoringFlags = !pair.second;
- if (wouldBlockActivitySwitchIgnoringFlags) {
- ActivityRecord topActivity = task.getActivity(ar ->
- !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
- FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
- /* caller_uid */
- callingUid,
- /* caller_activity_class_name */
- callerActivityClassName,
- /* target_task_top_activity_uid */
- topActivity == null ? -1 : topActivity.getUid(),
- /* target_task_top_activity_class_name */
- topActivity == null ? null : topActivity.info.name,
- /* target_task_is_different */
- false,
- /* target_activity_uid */
- -1,
- /* target_activity_class_name */
- null,
- /* target_intent_action */
- null,
- /* target_intent_flags */
- 0,
- /* action */
- FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK,
- /* version */
- 3,
- /* multi_window */
- false,
- /* bal_code */
- -1
- );
- }
- }
task.mTransitionController.requestCloseTransitionIfNeeded(task);
task.mInRemoveTask = true;
try {
@@ -1690,33 +1646,107 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
- if (wouldBlockActivitySwitchIgnoringFlags) {
- boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(callingUid)
- && shouldBlockActivitySwitchIfFeatureEnabled;
- if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
- UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- (restrictActivitySwitch
- ? "Returning home due to "
- : "Would return home due to ")
- + ActivitySecurityModelFeatureFlags.DOC_LINK,
- Toast.LENGTH_SHORT).show());
- }
-
- // If the activity switch should be restricted, return home rather than the
- // previously top task, to prevent users from being confused which app they're
- // viewing
- if (restrictActivitySwitch) {
- Slog.w(TAG, "Return to home as source uid: " + callingUid
- + "is not on top of task t: " + task);
- task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
- }
- }
+ checkActivitySecurityForTaskClear(callingUid, task, callerActivityClassName);
} finally {
task.mInRemoveTask = false;
}
}
+ // TODO(b/263368846) Move to live with the rest of the ASM logic.
+ /**
+ * Returns home if the passed in callingUid is not top of the stack, rather than returning to
+ * previous task.
+ */
+ private void checkActivitySecurityForTaskClear(int callingUid, Task task,
+ String callerActivityClassName) {
+ // We may have already checked that the callingUid has additional clearTask privileges, and
+ // cleared the calling identify. If so, we infer we do not need further restrictions here.
+ if (callingUid == SYSTEM_UID) {
+ return;
+ }
+
+ TaskDisplayArea displayArea = task.getTaskDisplayArea();
+ if (displayArea == null) {
+ // If there is no associated display area, we can not return home.
+ return;
+ }
+
+ Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task, callingUid, null);
+ boolean shouldBlockActivitySwitchIfFeatureEnabled = !pair.first;
+ boolean wouldBlockActivitySwitchIgnoringFlags = !pair.second;
+
+ if (!wouldBlockActivitySwitchIgnoringFlags) {
+ return;
+ }
+
+ ActivityRecord topActivity = task.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop());
+ FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+ /* caller_uid */
+ callingUid,
+ /* caller_activity_class_name */
+ callerActivityClassName,
+ /* target_task_top_activity_uid */
+ topActivity == null ? -1 : topActivity.getUid(),
+ /* target_task_top_activity_class_name */
+ topActivity == null ? null : topActivity.info.name,
+ /* target_task_is_different */
+ false,
+ /* target_activity_uid */
+ -1,
+ /* target_activity_class_name */
+ null,
+ /* target_intent_action */
+ null,
+ /* target_intent_flags */
+ 0,
+ /* action */
+ FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK,
+ /* version */
+ ActivitySecurityModelFeatureFlags.ASM_VERSION,
+ /* multi_window */
+ false,
+ /* bal_code */
+ -1
+ );
+
+ boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
+ .shouldRestrictActivitySwitch(callingUid)
+ && shouldBlockActivitySwitchIfFeatureEnabled;
+
+ PackageManager pm = mService.mContext.getPackageManager();
+ String callingPackage = pm.getNameForUid(callingUid);
+ final CharSequence callingLabel;
+ if (callingPackage == null) {
+ callingPackage = String.valueOf(callingUid);
+ callingLabel = callingPackage;
+ } else {
+ callingLabel = getApplicationLabel(pm, callingPackage);
+ }
+
+ if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
+ Toast toast = Toast.makeText(mService.mContext,
+ (ActivitySecurityModelFeatureFlags.DOC_LINK
+ + (restrictActivitySwitch
+ ? "returned home due to "
+ : "would return home due to ")
+ + callingLabel),
+ Toast.LENGTH_LONG);
+ UiThread.getHandler().post(toast::show);
+ }
+
+ // If the activity switch should be restricted, return home rather than the
+ // previously top task, to prevent users from being confused which app they're
+ // viewing
+ if (restrictActivitySwitch) {
+ Slog.w(TAG, "[ASM] Return to home as source: " + callingPackage
+ + " is not on top of task t: " + task);
+ displayArea.moveHomeActivityToTop("taskRemoved");
+ } else {
+ Slog.i(TAG, "[ASM] Would return to home as source: " + callingPackage
+ + " is not on top of task t: " + task);
+ }
+ }
+
/**
* For the purpose of ASM, ‘Top UID” for a task is defined as an activity UID
* 1. Which is top of the stack in z-order
@@ -1743,7 +1773,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Consider the source activity, whether or not it is finishing. Do not consider any other
// finishing activity.
Predicate<ActivityRecord> topOfStackPredicate = (ar) -> ar.equals(sourceRecord)
- || (!ar.isState(FINISHING) && !ar.isAlwaysOnTop());
+ || (!ar.finishing && !ar.isAlwaysOnTop());
// Check top of stack (or the first task fragment for embedding).
ActivityRecord topActivity = task.getActivity(topOfStackPredicate);
@@ -1777,6 +1807,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return topActivity.allowCrossUidActivitySwitchFromBelow(uid);
}
+ static CharSequence getApplicationLabel(PackageManager pm, String packageName) {
+ try {
+ ApplicationInfo launchedFromPackageInfo = pm.getApplicationInfo(
+ packageName, PackageManager.ApplicationInfoFlags.of(0));
+ return pm.getApplicationLabel(launchedFromPackageInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ return packageName;
+ }
+ }
+
void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
if (removeFromRecents) {
mRecentTasks.remove(task);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 587138ded8cb..8fc379724475 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -114,6 +114,35 @@ public class BackgroundActivityStartController {
/** Process belongs to a SDK sandbox */
static final int BAL_ALLOW_SDK_SANDBOX = 10;
+ static String balCodeToString(@BalCode int balCode) {
+ switch (balCode) {
+ case BAL_ALLOW_ALLOWLISTED_COMPONENT:
+ return "BAL_ALLOW_ALLOWLISTED_COMPONENT";
+ case BAL_ALLOW_ALLOWLISTED_UID:
+ return "BAL_ALLOW_ALLOWLISTED_UID";
+ case BAL_ALLOW_DEFAULT:
+ return "BAL_ALLOW_DEFAULT";
+ case BAL_ALLOW_FOREGROUND:
+ return "BAL_ALLOW_FOREGROUND";
+ case BAL_ALLOW_GRACE_PERIOD:
+ return "BAL_ALLOW_GRACE_PERIOD";
+ case BAL_ALLOW_PENDING_INTENT:
+ return "BAL_ALLOW_PENDING_INTENT";
+ case BAL_ALLOW_PERMISSION:
+ return "BAL_ALLOW_PERMISSION";
+ case BAL_ALLOW_SAW_PERMISSION:
+ return "BAL_ALLOW_SAW_PERMISSION";
+ case BAL_ALLOW_SDK_SANDBOX:
+ return "BAL_ALLOW_SDK_SANDBOX";
+ case BAL_ALLOW_VISIBLE_WINDOW:
+ return "BAL_ALLOW_VISIBLE_WINDOW";
+ case BAL_BLOCK:
+ return "BAL_BLOCK";
+ default:
+ throw new IllegalArgumentException("Unexpected value: " + balCode);
+ }
+ }
+
BackgroundActivityStartController(
final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) {
mService = service;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 25ce5699ab71..210a7d9538c7 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -344,6 +344,19 @@ class InsetsPolicy {
}
}
+ if (!attrs.isFullscreen() || attrs.getFitInsetsTypes() != 0) {
+ if (state == originalState) {
+ state = new InsetsState(originalState);
+ }
+ // Explicitly exclude floating windows from receiving caption insets. This is because we
+ // hard code caption insets for windows due to a synchronization issue that leads to
+ // flickering that bypasses insets frame calculation, which consequently needs us to
+ // remove caption insets from floating windows.
+ // TODO(b/254128050): Remove this workaround after we find a way to update window frames
+ // and caption insets frames simultaneously.
+ state.removeSource(InsetsState.ITYPE_CAPTION_BAR);
+ }
+
final SparseArray<WindowContainerInsetsSourceProvider> providers =
mStateController.getSourceProviders();
final int windowType = attrs.type;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 97e0b1e0de2c..ce9bff8521e6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -948,7 +948,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
synchronized (mService.mGlobalLock) {
WindowState windowState = mService.windowForClientLocked(this, window, false);
if (windowState == null) {
- Slog.e(TAG_WM,
+ Slog.i(TAG_WM,
"setOnBackInvokedCallback(): No window state for package:" + mPackageName);
} else {
windowState.setOnBackInvokedCallbackInfo(callbackInfo);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc81d526af5e..0a833f4f2dba 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3512,10 +3512,7 @@ class Task extends TaskFragment {
info.isKeyguardOccluded =
mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
- info.startingWindowTypeParameter = activity.mStartingData != null
- ? activity.mStartingData.mTypeParams
- : (StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED
- | StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS);
+ info.startingWindowTypeParameter = activity.mStartingData.mTypeParams;
if ((info.startingWindowTypeParameter
& StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 8cbd553fbebd..3cec3aa6a205 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1225,7 +1225,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Clear last paused activity if focused root task changed while sleeping, so that the
// top activity of current focused task can be resumed.
- if (mDisplayContent.isSleeping()) {
+ if (mDisplayContent.isSleeping() && currentFocusedTask != null) {
currentFocusedTask.clearLastPausedActivity();
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index b13136534de3..3a30e4b0cf1f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.TaskInfo.cameraCompatControlStateToString;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
@@ -44,7 +43,6 @@ import android.view.Display;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
-import android.window.IWindowlessStartingSurfaceCallback;
import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
@@ -658,10 +656,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
info.splashScreenThemeResId = launchTheme;
}
info.taskSnapshot = taskSnapshot;
- info.appToken = activity.token;
// make this happen prior than prepare surface
try {
- lastOrganizer.addStartingWindow(info);
+ lastOrganizer.addStartingWindow(info, activity.token);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskStart callback", e);
return false;
@@ -707,55 +704,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- /**
- * Create a starting surface which attach on a given surface.
- * @param activity Target activity, this isn't necessary to be the top activity.
- * @param root The root surface which the created surface will attach on.
- * @param taskSnapshot Whether to draw snapshot.
- * @param callback Called when surface is drawn and attached to the root surface.
- * @return The taskId, this is a token and should be used to remove the surface, even if
- * the task was removed from hierarchy.
- */
- int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root,
- TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) {
- final Task rootTask = task.getRootTask();
- if (rootTask == null) {
- return INVALID_TASK_ID;
- }
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
- if (lastOrganizer == null) {
- return INVALID_TASK_ID;
- }
- final StartingWindowInfo info = task.getStartingWindowInfo(activity);
- info.taskInfo.taskDescription = activity.taskDescription;
- info.taskSnapshot = taskSnapshot;
- info.windowlessStartingSurfaceCallback = callback;
- info.rootSurface = root;
- try {
- lastOrganizer.addStartingWindow(info);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending addWindowlessStartingSurface ", e);
- return INVALID_TASK_ID;
- }
- return task.mTaskId;
- }
-
- void removeWindowlessStartingSurface(int taskId, boolean immediately) {
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
- if (lastOrganizer == null || taskId == 0) {
- return;
- }
- final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
- removalInfo.taskId = taskId;
- removalInfo.windowlessSurface = true;
- removalInfo.removeImmediately = immediately;
- try {
- lastOrganizer.removeStartingWindow(removalInfo);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending removeWindowlessStartingSurface ", e);
- }
- }
-
boolean copySplashScreenView(Task task) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index e56b6792f491..7c0318d2bee4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -54,7 +54,6 @@ import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.app.servertransaction.ConfigurationChangeItem;
-import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -211,7 +210,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */
private volatile boolean mHasCachedConfiguration;
- private int mTopActivityDeviceId = VirtualDeviceManager.DEVICE_ID_DEFAULT;
+ private int mTopActivityDeviceId = Context.DEVICE_ID_DEFAULT;
/**
* Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not
* registered.
@@ -1435,7 +1434,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// TODO(b/263402938): Add tests that capture the deviceId dispatch to the client.
mTopActivityDeviceId = deviceId;
dispatchConfiguration(config, topActivityDeviceChanged ? mTopActivityDeviceId
- : VirtualDeviceManager.DEVICE_ID_INVALID);
+ : Context.DEVICE_ID_INVALID);
}
private int getTopActivityDeviceId() {
@@ -1520,7 +1519,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) {
// By default send invalid deviceId as no-op signal so it's not updated on the client side.
- scheduleConfigurationChange(thread, config, VirtualDeviceManager.DEVICE_ID_INVALID);
+ scheduleConfigurationChange(thread, config, Context.DEVICE_ID_INVALID);
}
private void scheduleConfigurationChange(IApplicationThread thread, Configuration config,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index e2bdcdd518d5..2ce86add4e58 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -179,6 +179,7 @@ cc_defaults {
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
+ "android.hardware.thermal-V1-ndk",
"android.hardware.tv.input@1.0",
"android.hardware.tv.input-V1-ndk",
"android.hardware.vibrator-V2-cpp",
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index ed79352bba21..7e0bb68c6168 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -16,27 +16,28 @@
#define LOG_TAG "HardwarePropertiesManagerService-JNI"
-#include <nativehelper/JNIHelp.h>
-#include "jni.h"
-
-#include <math.h>
-#include <stdlib.h>
-
+#include <aidl/android/hardware/thermal/IThermal.h>
+#include <android/binder_manager.h>
#include <android/hardware/thermal/1.0/IThermal.h>
+#include <math.h>
+#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include "core_jni_helpers.h"
+#include "jni.h"
namespace android {
+using ::aidl::android::hardware::thermal::CoolingDevice;
+using ::aidl::android::hardware::thermal::IThermal;
+using ::aidl::android::hardware::thermal::Temperature;
+using ::aidl::android::hardware::thermal::TemperatureThreshold;
+using ::aidl::android::hardware::thermal::TemperatureType;
+using ::aidl::android::hardware::thermal::ThrottlingSeverity;
using android::hidl::base::V1_0::IBase;
using hardware::hidl_death_recipient;
using hardware::hidl_vec;
-using hardware::thermal::V1_0::CoolingDevice;
-using hardware::thermal::V1_0::CpuUsage;
-using hardware::thermal::V1_0::IThermal;
-using hardware::thermal::V1_0::Temperature;
using hardware::thermal::V1_0::ThermalStatus;
using hardware::thermal::V1_0::ThermalStatusCode;
template<typename T>
@@ -62,20 +63,28 @@ jfloat gUndefinedTemperature;
static void getThermalHalLocked();
static std::mutex gThermalHalMutex;
-static sp<IThermal> gThermalHal = nullptr;
-
-// struct ThermalHalDeathRecipient;
-struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
- // hidl_death_recipient interface
- virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
- std::lock_guard<std::mutex> lock(gThermalHalMutex);
- ALOGE("ThermalHAL just died");
- gThermalHal = nullptr;
- getThermalHalLocked();
- }
+static sp<hardware::thermal::V1_0::IThermal> gThermalHidlHal = nullptr;
+static std::shared_ptr<IThermal> gThermalAidlHal = nullptr;
+
+struct ThermalHidlHalDeathRecipient : virtual public hidl_death_recipient {
+ // hidl_death_recipient interface
+ virtual void serviceDied(uint64_t cookie, const wp<IBase> &who) override {
+ std::lock_guard<std::mutex> lock(gThermalHalMutex);
+ ALOGE("Thermal HAL just died");
+ gThermalHidlHal = nullptr;
+ getThermalHalLocked();
+ }
};
-sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr;
+static void onThermalAidlBinderDied(void *cookie) {
+ std::lock_guard<std::mutex> lock(gThermalHalMutex);
+ ALOGE("Thermal AIDL HAL just died");
+ gThermalAidlHal = nullptr;
+ getThermalHalLocked();
+}
+
+sp<ThermalHidlHalDeathRecipient> gThermalHidlHalDeathRecipient = nullptr;
+ndk::ScopedAIBinder_DeathRecipient gThermalAidlDeathRecipient;
// ----------------------------------------------------------------------------
@@ -85,27 +94,49 @@ float finalizeTemperature(float temperature) {
// The caller must be holding gThermalHalMutex.
static void getThermalHalLocked() {
- if (gThermalHal != nullptr) {
+ if (gThermalAidlHal || gThermalHidlHal) {
+ return;
+ }
+ const std::string thermalInstanceName = std::string(IThermal::descriptor) + "/default";
+ if (AServiceManager_isDeclared(thermalInstanceName.c_str())) {
+ auto binder = AServiceManager_waitForService(thermalInstanceName.c_str());
+ auto thermalAidlService = IThermal::fromBinder(ndk::SpAIBinder(binder));
+ if (thermalAidlService) {
+ gThermalAidlHal = thermalAidlService;
+ if (gThermalAidlDeathRecipient.get() == nullptr) {
+ gThermalAidlDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+ AIBinder_DeathRecipient_new(onThermalAidlBinderDied));
+ }
+ auto linked = AIBinder_linkToDeath(thermalAidlService->asBinder().get(),
+ gThermalAidlDeathRecipient.get(), nullptr);
+ if (linked != STATUS_OK) {
+ ALOGW("Failed to link to death (AIDL): %d", linked);
+ gThermalAidlHal = nullptr;
+ }
+ } else {
+ ALOGE("Unable to get Thermal AIDL service");
+ }
return;
}
- gThermalHal = IThermal::getService();
+ ALOGI("Thermal AIDL service is not declared, trying HIDL");
+ gThermalHidlHal = hardware::thermal::V1_0::IThermal::getService();
- if (gThermalHal == nullptr) {
+ if (gThermalHidlHal == nullptr) {
ALOGE("Unable to get Thermal service.");
} else {
- if (gThermalHalDeathRecipient == nullptr) {
- gThermalHalDeathRecipient = new ThermalHalDeathRecipient();
+ if (gThermalHidlHalDeathRecipient == nullptr) {
+ gThermalHidlHalDeathRecipient = new ThermalHidlHalDeathRecipient();
}
- hardware::Return<bool> linked = gThermalHal->linkToDeath(
- gThermalHalDeathRecipient, 0x451F /* cookie */);
+ hardware::Return<bool> linked =
+ gThermalHidlHal->linkToDeath(gThermalHidlHalDeathRecipient, 0x451F /* cookie */);
if (!linked.isOk()) {
ALOGE("Transaction error in linking to ThermalHAL death: %s",
- linked.description().c_str());
- gThermalHal = nullptr;
+ linked.description().c_str());
+ gThermalHidlHal = nullptr;
} else if (!linked) {
ALOGW("Unable to link to ThermalHal death notifications");
- gThermalHal = nullptr;
+ gThermalHidlHal = nullptr;
} else {
ALOGD("Link to death notification successful");
}
@@ -117,17 +148,27 @@ static void nativeInit(JNIEnv* env, jobject obj) {
getThermalHalLocked();
}
-static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
- std::lock_guard<std::mutex> lock(gThermalHalMutex);
- getThermalHalLocked();
- if (gThermalHal == nullptr) {
- ALOGE("Couldn't get fan speeds because of HAL error.");
+static jfloatArray getFanSpeedsAidl(JNIEnv *env) {
+ std::vector<CoolingDevice> list;
+ auto status = gThermalAidlHal->getCoolingDevices(&list);
+ if (!status.isOk()) {
+ ALOGE("getFanSpeeds failed status: %s", status.getMessage());
return env->NewFloatArray(0);
}
+ float values[list.size()];
+ for (size_t i = 0; i < list.size(); ++i) {
+ values[i] = list[i].value;
+ }
+ jfloatArray fanSpeeds = env->NewFloatArray(list.size());
+ env->SetFloatArrayRegion(fanSpeeds, 0, list.size(), values);
+ return fanSpeeds;
+}
- hidl_vec<CoolingDevice> list;
- Return<void> ret = gThermalHal->getCoolingDevices(
- [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) {
+static jfloatArray getFanSpeedsHidl(JNIEnv *env) {
+ hidl_vec<hardware::thermal::V1_0::CoolingDevice> list;
+ Return<void> ret = gThermalHidlHal->getCoolingDevices(
+ [&list](ThermalStatus status,
+ hidl_vec<hardware::thermal::V1_0::CoolingDevice> devices) {
if (status.code == ThermalStatusCode::SUCCESS) {
list = std::move(devices);
} else {
@@ -137,9 +178,9 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
});
if (!ret.isOk()) {
- ALOGE("getCoolingDevices failed status: %s", ret.description().c_str());
+ ALOGE("getFanSpeeds failed status: %s", ret.description().c_str());
+ return env->NewFloatArray(0);
}
-
float values[list.size()];
for (size_t i = 0; i < list.size(); ++i) {
values[i] = list[i].currentValue;
@@ -149,17 +190,79 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
return fanSpeeds;
}
-static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type,
- int source) {
+static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
std::lock_guard<std::mutex> lock(gThermalHalMutex);
getThermalHalLocked();
- if (gThermalHal == nullptr) {
- ALOGE("Couldn't get device temperatures because of HAL error.");
+ if (!gThermalHidlHal && !gThermalAidlHal) {
+ ALOGE("Couldn't get fan speeds because of HAL error.");
return env->NewFloatArray(0);
}
- hidl_vec<Temperature> list;
- Return<void> ret = gThermalHal->getTemperatures(
- [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) {
+ if (gThermalAidlHal) {
+ return getFanSpeedsAidl(env);
+ }
+ return getFanSpeedsHidl(env);
+}
+
+static jfloatArray getDeviceTemperaturesAidl(JNIEnv *env, int type, int source) {
+ jfloat *values;
+ size_t length = 0;
+ if (source == TEMPERATURE_CURRENT) {
+ std::vector<Temperature> list;
+ auto status =
+ gThermalAidlHal->getTemperaturesWithType(static_cast<TemperatureType>(type), &list);
+
+ if (!status.isOk()) {
+ ALOGE("getDeviceTemperatures failed status: %s", status.getMessage());
+ return env->NewFloatArray(0);
+ }
+ values = new jfloat[list.size()];
+ for (const auto &temp : list) {
+ if (static_cast<int>(temp.type) == type) {
+ values[length++] = finalizeTemperature(temp.value);
+ }
+ }
+ } else if (source == TEMPERATURE_THROTTLING_BELOW_VR_MIN) {
+ values = new jfloat[1];
+ values[length++] = gUndefinedTemperature;
+ } else {
+ std::vector<TemperatureThreshold> list;
+ auto status =
+ gThermalAidlHal->getTemperatureThresholdsWithType(static_cast<TemperatureType>(
+ type),
+ &list);
+
+ if (!status.isOk()) {
+ ALOGE("getDeviceTemperatures failed status: %s", status.getMessage());
+ return env->NewFloatArray(0);
+ }
+ values = new jfloat[list.size()];
+ for (auto &t : list) {
+ if (static_cast<int>(t.type) == type) {
+ switch (source) {
+ case TEMPERATURE_THROTTLING:
+ values[length++] =
+ finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>(
+ ThrottlingSeverity::SEVERE)]);
+ break;
+ case TEMPERATURE_SHUTDOWN:
+ values[length++] =
+ finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>(
+ ThrottlingSeverity::SHUTDOWN)]);
+ break;
+ }
+ }
+ }
+ }
+ jfloatArray deviceTemps = env->NewFloatArray(length);
+ env->SetFloatArrayRegion(deviceTemps, 0, length, values);
+ return deviceTemps;
+}
+
+static jfloatArray getDeviceTemperaturesHidl(JNIEnv *env, int type, int source) {
+ hidl_vec<hardware::thermal::V1_0::Temperature> list;
+ Return<void> ret = gThermalHidlHal->getTemperatures(
+ [&list](ThermalStatus status,
+ hidl_vec<hardware::thermal::V1_0::Temperature> temperatures) {
if (status.code == ThermalStatusCode::SUCCESS) {
list = std::move(temperatures);
} else {
@@ -170,9 +273,9 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */,
if (!ret.isOk()) {
ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str());
+ return env->NewFloatArray(0);
}
-
- jfloat values[list.size()];
+ float values[list.size()];
size_t length = 0;
for (size_t i = 0; i < list.size(); ++i) {
if (static_cast<int>(list[i].type) == type) {
@@ -197,16 +300,34 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */,
return deviceTemps;
}
+static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type,
+ int source) {
+ std::lock_guard<std::mutex> lock(gThermalHalMutex);
+ getThermalHalLocked();
+ if (!gThermalHidlHal && !gThermalAidlHal) {
+ ALOGE("Couldn't get device temperatures because of HAL error.");
+ return env->NewFloatArray(0);
+ }
+ if (gThermalAidlHal) {
+ return getDeviceTemperaturesAidl(env, type, source);
+ }
+ return getDeviceTemperaturesHidl(env, type, source);
+}
+
static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) {
std::lock_guard<std::mutex> lock(gThermalHalMutex);
getThermalHalLocked();
- if (gThermalHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) {
+ if (gThermalAidlHal) {
+ ALOGW("getCpuUsages is not supported");
+ return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr);
+ }
+ if (gThermalHidlHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) {
ALOGE("Couldn't get CPU usages because of HAL error.");
return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr);
}
- hidl_vec<CpuUsage> list;
- Return<void> ret = gThermalHal->getCpuUsages(
- [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) {
+ hidl_vec<hardware::thermal::V1_0::CpuUsage> list;
+ Return<void> ret = gThermalHidlHal->getCpuUsages(
+ [&list](ThermalStatus status, hidl_vec<hardware::thermal::V1_0::CpuUsage> cpuUsages) {
if (status.code == ThermalStatusCode::SUCCESS) {
list = std::move(cpuUsages);
} else {
@@ -217,6 +338,7 @@ static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) {
if (!ret.isOk()) {
ALOGE("getCpuUsages failed status: %s", ret.description().c_str());
+ return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr);
}
jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz,
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index 356e9a95e311..a916b64fc0bd 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -17,10 +17,13 @@
#define LOG_TAG "NativeSensorService"
#include <android-base/properties.h>
+#include <android_os_NativeHandle.h>
#include <android_runtime/AndroidRuntime.h>
#include <core_jni_helpers.h>
+#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <jni.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <sensorservice/SensorService.h>
#include <string.h>
#include <utils/Log.h>
@@ -28,6 +31,8 @@
#include <mutex>
+#include "android_util_Binder.h"
+
#define PROXIMITY_ACTIVE_CLASS \
"com/android/server/sensors/SensorManagerInternal$ProximityActiveListener"
@@ -38,7 +43,10 @@ namespace android {
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnProximityActive;
-static jmethodID sMethodIdOnConfigurationChanged;
+static jmethodID sMethodIdRuntimeSensorOnConfigurationChanged;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelCreated;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelDestroyed;
+static jmethodID sMethodIdRuntimeSensorOnDirectChannelConfigured;
class NativeSensorService {
public:
@@ -47,7 +55,7 @@ public:
void registerProximityActiveListener();
void unregisterProximityActiveListener();
jint registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, jstring vendor,
- jobject callback);
+ jint flags, jobject callback);
void unregisterRuntimeSensor(jint handle);
jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
jfloatArray values);
@@ -74,6 +82,9 @@ private:
status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs,
int64_t batchReportLatencyNs) override;
+ int onDirectChannelCreated(int fd) override;
+ void onDirectChannelDestroyed(int channelHandle) override;
+ int onDirectChannelConfigured(int channelHandle, int sensorHandle, int rateLevel) override;
private:
jobject mCallback;
@@ -108,7 +119,7 @@ void NativeSensorService::unregisterProximityActiveListener() {
}
jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name,
- jstring vendor, jobject callback) {
+ jstring vendor, jint flags, jobject callback) {
if (mService == nullptr) {
ALOGD("Dropping registerRuntimeSensor, sensor service not available.");
return -1;
@@ -119,6 +130,11 @@ jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint
.vendor = env->GetStringUTFChars(vendor, 0),
.version = sizeof(sensor_t),
.type = type,
+#ifdef __LP64__
+ .flags = static_cast<uint64_t>(flags),
+#else
+ .flags = static_cast<uint32_t>(flags),
+#endif
};
sp<RuntimeSensorCallbackDelegate> callbackDelegate(
@@ -234,12 +250,39 @@ NativeSensorService::RuntimeSensorCallbackDelegate::~RuntimeSensorCallbackDelega
status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged(
int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) {
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
- return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged,
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnConfigurationChanged,
static_cast<jint>(handle), static_cast<jboolean>(enabled),
static_cast<jint>(ns2us(samplingPeriodNs)),
static_cast<jint>(ns2us(batchReportLatencyNs)));
}
+int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelCreated(int fd) {
+ if (fd <= 0) {
+ return 0;
+ }
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jobject jfd = jniCreateFileDescriptor(jniEnv, fd);
+ jobject parcelFileDescriptor = newParcelFileDescriptor(jniEnv, jfd);
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelCreated,
+ parcelFileDescriptor);
+}
+
+void NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelDestroyed(
+ int channelHandle) {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ return jniEnv->CallVoidMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelDestroyed,
+ static_cast<jint>(channelHandle));
+}
+
+int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelConfigured(int channelHandle,
+ int sensorHandle,
+ int rateLevel) {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelConfigured,
+ static_cast<jint>(channelHandle), static_cast<jint>(sensorHandle),
+ static_cast<jint>(rateLevel));
+}
+
static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) {
NativeSensorService* service = new NativeSensorService(env, listener);
return reinterpret_cast<jlong>(service);
@@ -256,9 +299,10 @@ static void unregisterProximityActiveListenerNative(JNIEnv* env, jclass, jlong p
}
static jint registerRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint deviceId, jint type,
- jstring name, jstring vendor, jobject callback) {
+ jstring name, jstring vendor, jint flags,
+ jobject callback) {
auto* service = reinterpret_cast<NativeSensorService*>(ptr);
- return service->registerRuntimeSensor(env, deviceId, type, name, vendor, callback);
+ return service->registerRuntimeSensor(env, deviceId, type, name, vendor, flags, callback);
}
static void unregisterRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint handle) {
@@ -280,7 +324,7 @@ static const JNINativeMethod methods[] = {
{"unregisterProximityActiveListenerNative", "(J)V",
reinterpret_cast<void*>(unregisterProximityActiveListenerNative)},
{"registerRuntimeSensorNative",
- "(JIILjava/lang/String;Ljava/lang/String;L" RUNTIME_SENSOR_CALLBACK_CLASS ";)I",
+ "(JIILjava/lang/String;Ljava/lang/String;IL" RUNTIME_SENSOR_CALLBACK_CLASS ";)I",
reinterpret_cast<void*>(registerRuntimeSensorNative)},
{"unregisterRuntimeSensorNative", "(JI)V",
reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
@@ -293,8 +337,17 @@ int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS);
sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V");
jclass runtimeSensorCallbackClass = FindClassOrDie(env, RUNTIME_SENSOR_CALLBACK_CLASS);
- sMethodIdOnConfigurationChanged =
+ sMethodIdRuntimeSensorOnConfigurationChanged =
GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I");
+ sMethodIdRuntimeSensorOnDirectChannelCreated =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelCreated",
+ "(Landroid/os/ParcelFileDescriptor;)I");
+ sMethodIdRuntimeSensorOnDirectChannelDestroyed =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelDestroyed", "(I)V");
+ sMethodIdRuntimeSensorOnDirectChannelConfigured =
+ GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelConfigured",
+ "(III)I");
+
return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
NELEM(methods));
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 8b913db35734..7a4e7dfd56a3 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.credentials;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
+import static android.Manifest.permission.LAUNCH_CREDENTIAL_SELECTOR;
import static android.content.Context.CREDENTIAL_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -631,7 +632,8 @@ public final class CredentialManagerService
}
// Send an intent to the UI that we have new enabled providers.
- getContext().sendBroadcast(IntentFactory.createProviderUpdateIntent());
+ getContext().sendBroadcast(IntentFactory.createProviderUpdateIntent(),
+ LAUNCH_CREDENTIAL_SELECTOR);
}
@Override
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 5e167712a8e8..32b14d773b95 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -44,7 +44,6 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
IGetCredentialCallback>
implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
private static final String TAG = "GetRequestSession";
-
public GetRequestSession(Context context, int userId, int callingUid,
IGetCredentialCallback callback, GetCredentialRequest request,
CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
@@ -173,6 +172,12 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
public void onProviderStatusChanged(ProviderSession.Status status,
ComponentName componentName) {
Log.i(TAG, "in onStatusChanged with status: " + status);
+ // Auth entry was selected, and it did not have any underlying credentials
+ if (status == ProviderSession.Status.NO_CREDENTIALS_FROM_AUTH_ENTRY) {
+ handleEmptyAuthenticationSelection(componentName);
+ return;
+ }
+ // For any other status, we check if all providers are done and then invoke UI if needed
if (!isAnyProviderPending()) {
// If all provider responses have been received, we can either need the UI,
// or we need to respond with error. The only other case is the entry being
@@ -186,4 +191,34 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
}
}
}
+
+ private void handleEmptyAuthenticationSelection(ComponentName componentName) {
+ // Update auth entry statuses across different provider sessions
+ mProviders.keySet().forEach(key -> {
+ ProviderGetSession session = (ProviderGetSession) mProviders.get(key);
+ if (!session.mComponentName.equals(componentName)) {
+ session.updateAuthEntriesStatusFromAnotherSession();
+ }
+ });
+
+ // Invoke UI since it needs to show a snackbar if last auth entry, or a status on each
+ // auth entries along with other valid entries
+ getProviderDataAndInitiateUi();
+
+ // Respond to client if all auth entries are empty and nothing else to show on the UI
+ if (providerDataContainsEmptyAuthEntriesOnly()) {
+ respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
+ "No credentials available");
+ }
+ }
+
+ private boolean providerDataContainsEmptyAuthEntriesOnly() {
+ for (String key : mProviders.keySet()) {
+ ProviderGetSession session = (ProviderGetSession) mProviders.get(key);
+ if (!session.containsEmptyAuthEntriesOnly()) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 12074c7494bf..6498b6afe208 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -241,13 +241,14 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
if (additionalContentReceived) {
Log.i(TAG, "Additional content received - removing authentication entry");
mProviderResponseDataHandler.removeAuthenticationAction(entryKey);
+ if (!mProviderResponseDataHandler.isEmptyResponse()) {
+ updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+ }
} else {
Log.i(TAG, "Additional content not received");
mProviderResponseDataHandler
.updateAuthEntryWithNoCredentialsReceived(entryKey);
- }
- if (!mProviderResponseDataHandler.isEmptyResponse()) {
- updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+ updateStatusAndInvokeCallback(Status.NO_CREDENTIALS_FROM_AUTH_ENTRY);
}
break;
case REMOTE_ENTRY_KEY:
@@ -414,25 +415,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Returns true if either an exception or a response is found. */
private void onActionEntrySelected(ProviderPendingIntentResponse
providerPendingIntentResponse) {
- // Action entry is expected to either contain the final GetCredentialResponse, or it is
- // also acceptable if it does not contain anything. In the second case, we re-show this
- // action on the UI.
- if (providerPendingIntentResponse == null) {
- Log.i(TAG, "providerPendingIntentResponse is null");
- return;
- }
-
- GetCredentialException exception = maybeGetPendingIntentException(
- providerPendingIntentResponse);
- if (exception != null) {
- invokeCallbackWithError(exception.getType(), exception.getMessage());
- }
- GetCredentialResponse response = PendingIntentResultHandler
- .extractGetCredentialResponse(
- providerPendingIntentResponse.getResultData());
- if (response != null) {
- mCallbacks.onFinalResponseReceived(mComponentName, response);
- }
+ Log.i(TAG, "onActionEntrySelected");
+ onCredentialEntrySelected(providerPendingIntentResponse);
}
@@ -449,11 +433,32 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/**
* When an invalid state occurs, e.g. entry mismatch or no response from provider,
- * we send back a TYPE_UNKNOWN error as to the developer.
+ * we send back a TYPE_NO_CREDENTIAL error as to the developer.
*/
private void invokeCallbackOnInternalInvalidState() {
mCallbacks.onFinalErrorReceived(mComponentName,
- GetCredentialException.TYPE_UNKNOWN, null);
+ GetCredentialException.TYPE_NO_CREDENTIAL, null);
+ }
+
+ /** Update auth entries status based on an auth entry selected from a different session. */
+ public void updateAuthEntriesStatusFromAnotherSession() {
+ // Pass null for entryKey if the auth entry selected belongs to a different session
+ mProviderResponseDataHandler.updateAuthEntryWithNoCredentialsReceived(/*entryKey=*/null);
+ }
+
+ /** Returns true if the provider response contains empty auth entries only, false otherwise. **/
+ public boolean containsEmptyAuthEntriesOnly() {
+ // We do not consider action entries here because if actions are the only entries,
+ // we don't show the UI
+ return mProviderResponseDataHandler.mUiCredentialEntries.isEmpty()
+ && mProviderResponseDataHandler.mUiRemoteEntry == null
+ && mProviderResponseDataHandler.mUiAuthenticationEntries
+ .values().stream().allMatch(
+ e -> e.second.getStatus() == AuthenticationEntry
+ .STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT
+ || e.second.getStatus()
+ == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
+ );
}
private class ProviderResponseDataHandler {
@@ -610,7 +615,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
? null : mUiCredentialEntries.get(entryKey).first;
}
- public void updateAuthEntryWithNoCredentialsReceived(String entryKey) {
+ public void updateAuthEntryWithNoCredentialsReceived(@Nullable String entryKey) {
+ if (entryKey == null) {
+ // Auth entry from a different provider was selected by the user.
+ updatePreviousMostRecentAuthEntry();
+ return;
+ }
updatePreviousMostRecentAuthEntry();
updateMostRecentAuthEntry(entryKey);
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 2f9d57872c3c..ecddcf30f88d 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -66,7 +66,8 @@ public abstract class ProviderSession<T, R>
* on the credMan UI.
*/
public static boolean isUiInvokingStatus(Status status) {
- return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED;
+ return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED
+ || status == Status.NO_CREDENTIALS_FROM_AUTH_ENTRY;
}
/**
@@ -140,7 +141,7 @@ public abstract class ProviderSession<T, R>
PENDING_INTENT_INVOKED,
CREDENTIAL_RECEIVED_FROM_SELECTION,
SAVE_ENTRIES_RECEIVED, CANCELED,
- NO_CREDENTIALS, EMPTY_RESPONSE, COMPLETE
+ NO_CREDENTIALS, EMPTY_RESPONSE, NO_CREDENTIALS_FROM_AUTH_ENTRY, COMPLETE
}
/** Converts exception to a provider session status. */
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index f0d05c5dfd05..f8bbfcfb6a4d 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -216,7 +216,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan
* Returns true if at least one provider is ready for UI invocation, and no
* provider is pending a response.
*/
- boolean isUiInvocationNeeded() {
+ protected boolean isUiInvocationNeeded() {
for (ProviderSession session : mProviders.values()) {
if (ProviderSession.isUiInvokingStatus(session.getStatus())) {
return true;
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 05a8b11830f7..07ddda39649a 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -52,6 +52,10 @@ android_test {
"android.test.runner",
],
+ data: [
+ ":SimpleTestIme",
+ ],
+
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/services/tests/InputMethodSystemServerTests/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/AndroidTest.xml
index 92be78060da8..13719343ac49 100644
--- a/services/tests/InputMethodSystemServerTests/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="FrameworksInputMethodSystemServerTests.apk" />
+ <option name="test-file-name" value="SimpleTestIme.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksInputMethodSystemServerTests" />
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java
new file mode 100644
index 000000000000..7cbfc52c5cce
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.util.ArrayMap;
+import android.view.inputmethod.InputMethod;
+import android.view.inputmethod.InputMethodInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class InputMethodManagerServiceRestrictImeAmountTest extends
+ InputMethodManagerServiceTestBase {
+
+ @Test
+ public void testFilterInputMethodServices_loadsAllImesBelowThreshold() {
+ List<ResolveInfo> resolveInfoList = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ resolveInfoList.add(
+ createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i));
+ }
+
+ final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList,
+ List.of());
+ assertEquals(5, methodList.size());
+ }
+
+ @Test
+ public void testFilterInputMethodServices_ignoresImesBeyondThreshold() {
+ List<ResolveInfo> resolveInfoList = new ArrayList<>();
+ for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) {
+ resolveInfoList.add(
+ createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i));
+ }
+
+ final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList,
+ List.of());
+ assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo(
+ InputMethodInfo.MAX_IMES_PER_PACKAGE);
+ }
+
+ @Test
+ public void testFilterInputMethodServices_loadsSystemImesBeyondThreshold() {
+ List<ResolveInfo> resolveInfoList = new ArrayList<>();
+ for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) {
+ resolveInfoList.add(
+ createFakeSystemResolveInfo("com.android.apps.inputmethod.systemime",
+ "SystemIME" + i));
+ }
+
+ final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList,
+ List.of());
+ assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo(
+ 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE);
+ }
+
+ @Test
+ public void testFilterInputMethodServices_ignoresImesBeyondThresholdFromTwoPackages() {
+ List<ResolveInfo> resolveInfoList = new ArrayList<>();
+ for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) {
+ resolveInfoList.add(
+ createFakeResolveInfo("com.android.apps.inputmethod.simpleime1", "IME1_" + i));
+ }
+ for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) {
+ resolveInfoList.add(
+ createFakeResolveInfo("com.android.apps.inputmethod.simpleime2", "IME2_" + i));
+ }
+
+ final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList,
+ List.of());
+ assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo(
+ 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE);
+ }
+
+ @Test
+ public void testFilterInputMethodServices_stillLoadsEnabledImesBeyondThreshold() {
+ final ResolveInfo enabledIme = createFakeResolveInfo(
+ "com.android.apps.inputmethod.simpleime_enabled", "EnabledIME");
+
+ List<ResolveInfo> resolveInfoList = new ArrayList<>();
+ for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) {
+ resolveInfoList.add(
+ createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i));
+ }
+ resolveInfoList.add(enabledIme);
+
+ final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList,
+ List.of(new ComponentName(enabledIme.serviceInfo.packageName,
+ enabledIme.serviceInfo.name).flattenToShortString()));
+
+ assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo(
+ 1 + InputMethodInfo.MAX_IMES_PER_PACKAGE);
+ }
+
+ private List<InputMethodInfo> filterInputMethodServices(List<ResolveInfo> resolveInfoList,
+ List<String> enabledComponents) {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ InputMethodManagerService.filterInputMethodServices(new ArrayMap<>(), methodMap, methodList,
+ enabledComponents, mContext, resolveInfoList);
+ return methodList;
+ }
+
+ private ResolveInfo createFakeSystemResolveInfo(String packageName, String componentName) {
+ final ResolveInfo ime = createFakeResolveInfo(packageName, componentName);
+ ime.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ return ime;
+ }
+
+ private ResolveInfo createFakeResolveInfo(String packageName, String componentName) {
+ final ResolveInfo ime = getResolveInfo("com.android.apps.inputmethod.simpleime");
+ if (packageName != null) {
+ ime.serviceInfo.packageName = packageName;
+ }
+ if (componentName != null) {
+ ime.serviceInfo.name = componentName;
+ }
+ return ime;
+ }
+
+ private ResolveInfo getResolveInfo(String packageName) {
+ final int flags = PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ final List<ResolveInfo> ime = mContext.getPackageManager().queryIntentServices(
+ new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
+ PackageManager.ResolveInfoFlags.of(flags));
+ assertWithMessage("Loaded IMEs").that(ime.size()).isGreaterThan(0);
+ return ime.get(0);
+ }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index dbdffd05714a..9501b9604fa0 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -127,7 +127,7 @@ public class InputMethodManagerServiceTestBase {
mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
- .mockStatic(LocalServices.class)
+ .spyStatic(LocalServices.class)
.mockStatic(ServiceManager.class)
.mockStatic(SystemServerInitThreadPool.class)
.startMocking();
@@ -212,6 +212,7 @@ public class InputMethodManagerServiceTestBase {
new InputMethodManagerService.Lifecycle(mContext, mInputMethodManagerService);
// Public local InputMethodManagerService.
+ LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
lifecycle.onStart();
try {
// After this boot phase, services can broadcast Intents.
@@ -237,6 +238,7 @@ public class InputMethodManagerServiceTestBase {
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
+ LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
}
protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput)
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
new file mode 100644
index 000000000000..111cabd298f5
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase {
+ @Test
+ public void testSwitchToNextKeyboardLayout() {
+ ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController);
+ InputMethodManagerInternal.get().switchKeyboardLayout(1);
+ verify(mInputMethodManagerService.mSwitchingController)
+ .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 95c2ed2f841f..99da415380cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -759,40 +759,6 @@ public final class BroadcastQueueModernImplTest {
}
/**
- * Verify that sending a broadcast that removes any matching pending
- * broadcasts is applied as expected.
- */
- @Test
- public void testRemoveMatchingFilter() {
- final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
- final BroadcastOptions optionsOn = BroadcastOptions.makeBasic();
- optionsOn.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_OFF));
-
- final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
- final BroadcastOptions optionsOff = BroadcastOptions.makeBasic();
- optionsOff.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_ON));
-
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
-
- // While we're here, give our health check some test coverage
- mImpl.checkHealthLocked();
-
- // Marching through the queue we should only have one SCREEN_OFF
- // broadcast, since that's the last state we dispatched
- final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN,
- getUidForPackage(PACKAGE_GREEN));
- queue.makeActiveNextPending();
- assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction());
- assertTrue(queue.isEmpty());
- }
-
- /**
* Verify that sending a broadcast with DELIVERY_GROUP_POLICY_MOST_RECENT works as expected.
*/
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 95a588497980..5f82ec1dde02 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -53,6 +53,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
+import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 991d5667206b..8b420a36602c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -234,15 +234,9 @@ public class JobSchedulerServiceTest {
createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH));
JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs",
createJobInfo(6));
- JobStatus jobDT = createJobStatus("testGetMinJobExecutionGuaranteeMs",
- createJobInfo(7)
- .setDataTransfer(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
- JobStatus jobUI = createJobStatus("testGetMinJobExecutionGuaranteeMs",
- createJobInfo(8)); // TODO(255371817): add setUserInitiated(true)
JobStatus jobUIDT = createJobStatus("testGetMinJobExecutionGuaranteeMs",
- // TODO(255371817): add setUserInitiated(true)
createJobInfo(9)
- .setDataTransfer(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
+ .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
spyOn(ejMax);
spyOn(ejHigh);
@@ -250,8 +244,6 @@ public class JobSchedulerServiceTest {
spyOn(ejHighDowngraded);
spyOn(jobHigh);
spyOn(jobDef);
- spyOn(jobDT);
- spyOn(jobUI);
spyOn(jobUIDT);
when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true);
@@ -260,14 +252,11 @@ public class JobSchedulerServiceTest {
when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false);
when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false);
- when(jobUI.shouldTreatAsUserInitiatedJob()).thenReturn(true);
when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
ConnectivityController connectivityController = mService.getConnectivityController();
spyOn(connectivityController);
mService.mConstants.RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
- mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS = 15 * MINUTE_IN_MILLIS;
- mService.mConstants.RUNTIME_DATA_TRANSFER_LIMIT_MS = 60 * MINUTE_IN_MILLIS;
mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.5f;
mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS = HOUR_IN_MILLIS;
mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS = 6 * HOUR_IN_MILLIS;
@@ -284,37 +273,14 @@ public class JobSchedulerServiceTest {
mService.getMinJobExecutionGuaranteeMs(jobHigh));
assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(jobDef));
- grantRunUserInitiatedJobsPermission(false); // Without permission
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobDT));
- grantRunUserInitiatedJobsPermission(true); // With permission
- doReturn(ConnectivityController.UNKNOWN_TIME)
- .when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobDT));
- doReturn(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS / 2)
- .when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobDT));
- doReturn(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS * 2)
- .when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobDT));
- doReturn(mService.mConstants.RUNTIME_DATA_TRANSFER_LIMIT_MS * 2)
- .when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobDT));
// UserInitiated
grantRunUserInitiatedJobsPermission(false);
- // Permission isn't granted, so it should just be treated as a regular data transfer job.
- assertEquals(mService.mConstants.RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobUIDT));
// Permission isn't granted, so it should just be treated as a regular job.
assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobUI));
+ mService.getMinJobExecutionGuaranteeMs(jobUIDT));
grantRunUserInitiatedJobsPermission(true); // With permission
- assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
- mService.getMinJobExecutionGuaranteeMs(jobUI));
+ assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(jobUIDT));
doReturn(ConnectivityController.UNKNOWN_TIME)
.when(connectivityController).getEstimatedTransferTimeMs(any());
assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
@@ -338,21 +304,10 @@ public class JobSchedulerServiceTest {
@Test
public void testGetMaxJobExecutionTimeMs() {
- JobStatus jobDT = createJobStatus("testGetMaxJobExecutionTimeMs",
- createJobInfo(7)
- .setDataTransfer(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
- JobStatus jobUI = createJobStatus("testGetMaxJobExecutionTimeMs",
- createJobInfo(9)); // TODO(255371817): add setUserInitiated(true)
JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs",
- // TODO(255371817): add setUserInitiated(true)
createJobInfo(10)
- .setDataTransfer(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
-
- spyOn(jobDT);
- spyOn(jobUI);
+ .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
spyOn(jobUIDT);
-
- when(jobUI.shouldTreatAsUserInitiatedJob()).thenReturn(true);
when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
QuotaController quotaController = mService.getQuotaController();
@@ -365,17 +320,9 @@ public class JobSchedulerServiceTest {
.when(quotaController).getMaxJobExecutionTimeMsLocked(any());
grantRunUserInitiatedJobsPermission(true);
- assertEquals(mService.mConstants.RUNTIME_DATA_TRANSFER_LIMIT_MS,
- mService.getMaxJobExecutionTimeMs(jobDT));
- assertEquals(mService.mConstants.RUNTIME_USER_INITIATED_LIMIT_MS,
- mService.getMaxJobExecutionTimeMs(jobUI));
assertEquals(mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUIDT));
grantRunUserInitiatedJobsPermission(false);
- assertEquals(mService.mConstants.RUNTIME_DATA_TRANSFER_LIMIT_MS,
- mService.getMaxJobExecutionTimeMs(jobDT));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
- mService.getMaxJobExecutionTimeMs(jobUI));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUIDT));
}
@@ -478,7 +425,8 @@ public class JobSchedulerServiceTest {
@Test
public void testGetRescheduleJobForFailure_userStopped() {
JobStatus uiJob = createJobStatus("testGetRescheduleJobForFailure",
- createJobInfo().setUserInitiated(true));
+ createJobInfo().setUserInitiated(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
JobStatus uvJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo());
spyOn(uvJob);
doReturn(true).when(uvJob).isUserVisibleJob();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 1e65b380310f..6bc552c7f32e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -619,7 +619,7 @@ public class FlexibilityControllerTest {
@Test
public void testExceptions_UserInitiated() {
JobInfo.Builder jb = createJob(0);
- jb.setUserInitiated(true);
+ jb.setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
JobStatus js = createJobStatus("testExceptions_UserInitiated", jb);
assertFalse(js.hasFlexibilityConstraint());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index e6bc72f94f05..5dc8ed58994d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -170,6 +170,7 @@ public class JobStatusTest {
final JobInfo jobInfo =
new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
JobStatus job = createJobStatus(jobInfo);
assertTrue(job.canRunInBatterySaver());
@@ -216,6 +217,7 @@ public class JobStatusTest {
final JobInfo jobInfo =
new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
JobStatus job = createJobStatus(jobInfo);
assertTrue(job.canRunInDoze());
@@ -236,6 +238,7 @@ public class JobStatusTest {
// User-initiated jobs are always user-visible unless they've been demoted.
jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
job = createJobStatus(jobInfo);
@@ -507,6 +510,7 @@ public class JobStatusTest {
jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setUserInitiated(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
job = createJobStatus(jobInfo);
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 3f5d1139c9db..06ba5dd6069b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -664,7 +664,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
whenever(mocks.componentResolver.queryActivities(any(),
argThat { intent: Intent? -> intent != null && (action == intent.action) },
- nullable(), anyLong(), anyInt(), anyInt())) {
+ nullable(), anyLong(), anyInt())) {
ArrayList(activities.asList().map { info: ActivityInfo? ->
ResolveInfo().apply { activityInfo = info }
})
@@ -674,7 +674,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
whenever(mocks.componentResolver.queryServices(any(),
argThat { intent: Intent? -> intent != null && (action == intent.action) },
- nullable(), anyLong(), anyInt(), anyInt())) {
+ nullable(), anyLong(), anyInt())) {
ArrayList(services.asList().map { info ->
ResolveInfo().apply { serviceInfo = info }
})
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 503e579062d4..96eca7171af0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -289,7 +289,7 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
- public void testRegisterProxyWithoutPermission() throws Exception {
+ public void testRegisterProxyWithoutA11yPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -301,6 +301,18 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
+ public void testRegisterProxyWithoutDevicePermission() throws Exception {
+ doThrow(SecurityException.class).when(mMockSecurityPolicy)
+ .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE);
+
+ assertThrows(SecurityException.class,
+ () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
+ verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
+ any(), any(), any());
+ }
+
+ @SmallTest
+ @Test
public void testRegisterProxyForDefaultDisplay() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
@@ -328,7 +340,7 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
- public void testUnRegisterProxyWithoutPermission() throws Exception {
+ public void testUnRegisterProxyWithoutA11yPermission() {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -339,6 +351,17 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
+ public void testUnRegisterProxyWithoutDevicePermission() {
+ doThrow(SecurityException.class).when(mMockSecurityPolicy)
+ .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE);
+
+ assertThrows(SecurityException.class,
+ () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
+ verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
+ }
+
+ @SmallTest
+ @Test
public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 51d3bae7d32c..306ce4dbf707 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -44,6 +44,8 @@ import android.annotation.NonNull;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -507,6 +509,91 @@ public class FullScreenMagnificationGestureHandlerTest {
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
}
+ @Test
+ public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
+ final float scale = 2.0f;
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float persistedScale = (1.0f + threshold) * scale + 1.0f;
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mFullScreenMagnificationController.persistScale(DISPLAY_0);
+ mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testTransitToPanningState_scaleDifferenceLessThanThreshold_doNotDetect() {
+ final float scale = 2.0f;
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float persistedScale = (1.0f + threshold) * scale - 0.1f;
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mFullScreenMagnificationController.persistScale(DISPLAY_0);
+ mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ assertFalse(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testPanningScaleToPersistedScale_detecting_vibrateAndClear() {
+ Vibrator vibrator = mock(Vibrator.class);
+ mContext.addMockSystemService(Vibrator.class, vibrator);
+
+ mMgh.mPanningScalingState.mDetectingPassPersistedScale = true;
+
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+ mMgh.mPanningScalingState.setScaleAndClearIfNeeded(persistedScale, DEFAULT_X, DEFAULT_Y);
+
+ verify(vibrator).vibrate(any(VibrationEffect.class));
+ assertFalse(mMgh.mPanningScalingState.mScaling);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testPanningScaleOverThreshold_notDetecting_startDetecting() {
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float scale = (1.0f + threshold) * persistedScale + 1.0f;
+ mMgh.mPanningScalingState.setScaleAndClearIfNeeded(scale, DEFAULT_X, DEFAULT_Y);
+
+ assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 1298e7bbd344..24b003c2c011 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.nullable;
import static org.mockito.Mockito.times;
@@ -37,6 +38,7 @@ import android.accounts.AccountManagerInternal;
import android.accounts.CantAddAccountActivity;
import android.accounts.IAccountManagerResponse;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.INotificationManager;
import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
@@ -171,6 +173,16 @@ public class AccountManagerServiceTest extends AndroidTestCase {
setContext(mockContext);
mTestInjector = new TestInjector(realTestContext, mockContext, mMockNotificationManager);
mAms = new AccountManagerService(mTestInjector);
+ doAnswer(invocation -> {
+ final Intent intent = invocation.getArgument(0);
+ final Bundle options = invocation.getArgument(3);
+ if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.endsWith(intent.getAction())) {
+ final BroadcastOptions bOptions = new BroadcastOptions(options);
+ assertEquals(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT,
+ bOptions.getDeliveryGroupPolicy());
+ }
+ return null;
+ }).when(mMockContext).sendBroadcastAsUser(any(), any(), any(), any());
}
@Override
@@ -3142,7 +3154,7 @@ public class AccountManagerServiceTest extends AndroidTestCase {
mAccountRemovedBroadcasts = 0;
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, atLeast(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(),
- any(UserHandle.class));
+ any(UserHandle.class), any(), any());
for (Intent intent : captor.getAllValues()) {
if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED.equals(intent.getAction())) {
mVisibleAccountsChangedBroadcasts++;
@@ -3499,7 +3511,19 @@ public class AccountManagerServiceTest extends AndroidTestCase {
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
- mMockContext.sendBroadcastAsUser(intent, user);
+ sendBroadcastAsUser(intent, user, null, null);
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ Bundle options) {
+ mMockContext.sendBroadcastAsUser(intent, user, receiverPermission, options);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return mMockContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index b0c3a6e26b7a..e9d82696affc 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -37,9 +37,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.biometrics.AuthenticateOptions;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricService;
+import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -48,6 +51,7 @@ import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
+import android.util.Slog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -58,6 +62,8 @@ import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import org.junit.Before;
import org.junit.Rule;
@@ -67,6 +73,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.function.Supplier;
@Presubmit
@@ -78,6 +87,9 @@ public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
private static final int TEST_SENSOR_ID = 1;
private static final int LOG_NUM_RECENT_OPERATIONS = 2;
+ private static final Fingerprint TEST_FINGERPRINT = new Fingerprint("" /* name */,
+ 1 /* fingerId */, TEST_SENSOR_ID);
+
@Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getContext(), null);
@@ -673,6 +685,56 @@ public class BiometricSchedulerTest {
}
+ @Test
+ public void testTwoInternalCleanupOps_withFirstFavorHalEnrollment() throws Exception {
+ final String owner = "test.owner";
+ final int userId = 1;
+ final Supplier<Object> daemon = () -> mock(AidlSession.class);
+ final FingerprintUtils utils = mock(FingerprintUtils.class);
+ final Map<Integer, Long> authenticatorIds = new HashMap<>();
+ final ClientMonitorCallback callback0 = mock(ClientMonitorCallback.class);
+ final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+ final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
+
+ final TestInternalCleanupClient client1 = new
+ TestInternalCleanupClient(mContext, daemon, userId,
+ owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+ mBiometricContext, utils, authenticatorIds);
+ final TestInternalCleanupClient client2 = new
+ TestInternalCleanupClient(mContext, daemon, userId,
+ owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
+ mBiometricContext, utils, authenticatorIds);
+
+ //add initial start client to scheduler, so later clients will be on pending operation queue
+ final TestHalClientMonitor startClient = new TestHalClientMonitor(mContext, mToken,
+ daemon);
+ mScheduler.scheduleClientMonitor(startClient, callback0);
+
+ //add first cleanup client which favors enrollments from HAL
+ client1.setFavorHalEnrollments();
+ mScheduler.scheduleClientMonitor(client1, callback1);
+ assertEquals(1, mScheduler.mPendingOperations.size());
+
+ when(utils.getBiometricsForUser(mContext, userId)).thenAnswer(i ->
+ new ArrayList<>(client1.getFingerprints()));
+
+ //add second cleanup client
+ mScheduler.scheduleClientMonitor(client2, callback2);
+
+ //finish the start client, so other pending clients are processed
+ startClient.getCallback().onClientFinished(startClient, true);
+
+ waitForIdle();
+
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client2.isAlreadyDone());
+ assertNull(mScheduler.mCurrentOperation);
+ verify(utils, never()).removeBiometricForUser(mContext, userId,
+ TEST_FINGERPRINT.getBiometricId());
+ assertEquals(1, client1.getFingerprints().size());
+ }
+
+
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
@@ -681,7 +743,37 @@ public class BiometricSchedulerTest {
TestableLooper.get(this).processAllMessages();
}
- private static class TestAuthenticationClient extends AuthenticationClient<Object> {
+ private static class TestAuthenticateOptions implements AuthenticateOptions {
+ @Override
+ public int getUserId() {
+ return 0;
+ }
+
+ @Override
+ public int getSensorId() {
+ return TEST_SENSOR_ID;
+ }
+
+ @Override
+ public int getDisplayState() {
+ return DISPLAY_STATE_UNKNOWN;
+ }
+
+ @NonNull
+ @Override
+ public String getOpPackageName() {
+ return "some.test.name";
+ }
+
+ @Nullable
+ @Override
+ public String getAttributionTag() {
+ return null;
+ }
+ }
+
+ private static class TestAuthenticationClient
+ extends AuthenticationClient<Object, TestAuthenticateOptions> {
boolean mStartedHal = false;
boolean mStoppedHal = false;
boolean mDestroyed = false;
@@ -700,9 +792,10 @@ public class BiometricSchedulerTest {
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int cookie,
@NonNull BiometricContext biometricContext) {
- super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
- false /* restricted */, TAG, cookie, false /* requireConfirmation */,
- TEST_SENSOR_ID, mock(BiometricLogger.class), biometricContext,
+ super(context, lazyDaemon, token, listener, 0 /* operationId */,
+ false /* restricted */, new TestAuthenticateOptions(), cookie,
+ false /* requireConfirmation */,
+ mock(BiometricLogger.class), biometricContext,
true /* isStrongBiometric */, null /* taskStackListener */,
null /* lockoutTracker */, false /* isKeyguard */,
true /* shouldVibrate */,
@@ -835,4 +928,90 @@ public class BiometricSchedulerTest {
mDestroyed = true;
}
}
+
+ private static class TestInternalEnumerateClient extends InternalEnumerateClient<Object> {
+ private static final String TAG = "TestInternalEnumerateClient";
+
+
+ protected TestInternalEnumerateClient(@NonNull Context context,
+ @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, int userId,
+ @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ logger, biometricContext);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ Slog.d(TAG, "TestInternalEnumerateClient#startHalOperation");
+ onEnumerationResult(TEST_FINGERPRINT, 0 /* remaining */);
+ }
+ }
+
+ private static class TestRemovalClient extends RemovalClient<Fingerprint, Object> {
+ private static final String TAG = "TestRemovalClient";
+
+ TestRemovalClient(@NonNull Context context,
+ @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
+ @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
+ @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
+ logger, biometricContext, authenticatorIds);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ Slog.d(TAG, "Removing template from hw");
+ onRemoved(TEST_FINGERPRINT, 0);
+ }
+ }
+
+ private static class TestInternalCleanupClient extends
+ InternalCleanupClient<Fingerprint, Object> {
+ private List<Fingerprint> mFingerprints = new ArrayList<>();
+
+ TestInternalCleanupClient(@NonNull Context context,
+ @NonNull Supplier<Object> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
+ utils, authenticatorIds);
+ }
+
+ @Override
+ protected InternalEnumerateClient<Object> getEnumerateClient(Context context,
+ Supplier<Object> lazyDaemon, IBinder token, int userId, String owner,
+ List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ return new TestInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId,
+ logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE),
+ biometricContext);
+ }
+
+ @Override
+ protected RemovalClient<Fingerprint, Object> getRemovalClient(Context context,
+ Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId,
+ String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ Map<Integer, Long> authenticatorIds) {
+ return new TestRemovalClient(context, lazyDaemon, token, null,
+ new int[]{biometricId}, userId, owner, utils, sensorId, logger,
+ biometricContext, authenticatorIds);
+ }
+
+ @Override
+ protected void onAddUnknownTemplate(int userId,
+ @NonNull BiometricAuthenticator.Identifier identifier) {
+ mFingerprints.add((Fingerprint) identifier);
+ }
+
+ public List<Fingerprint> getFingerprints() {
+ return mFingerprints;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 184a5562a689..3ff802c0125c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -33,6 +33,7 @@ import android.content.ComponentName;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticateOptions;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -153,14 +154,18 @@ public class FaceAuthenticationClientTest {
when(mHal.getInterfaceVersion()).thenReturn(version);
final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder()
+ .setOpPackageName("test-owner")
+ .setUserId(5)
+ .setSensorId(9)
+ .build();
return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
- 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
- false /* restricted */, "test-owner", 4 /* cookie */,
- false /* requireConfirmation */, 9 /* sensorId */,
+ 2 /* requestId */, mClientMonitorCallbackConverter, OP_ID,
+ false /* restricted */, options, 4 /* cookie */,
+ false /* requireConfirmation */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
mUsageStats, null /* mLockoutCache */, false /* allowBackgroundAuthentication */,
- null /* sensorPrivacyManager */,
- 0 /* biometricStrength */) {
+ null /* sensorPrivacyManager */, 0 /* biometricStrength */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index e0fdb8cfc74e..c4c550549f73 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticateOptions;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -112,8 +113,13 @@ public class FaceDetectClientTest {
final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
return new FaceDetectClient(mContext, () -> aidl, mToken,
- 99 /* requestId */, mClientMonitorCallbackConverter, USER_ID,
- "own-it", 5 /* sensorId */, mBiometricLogger, mBiometricContext,
+ 99 /* requestId */, mClientMonitorCallbackConverter,
+ new FaceAuthenticateOptions.Builder()
+ .setUserId(USER_ID)
+ .setSensorId(5)
+ .setOpPackageName("own-it")
+ .build(),
+ mBiometricLogger, mBiometricContext,
false /* isStrongBiometric */, null /* sensorPrivacyManager */);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 3b66eaba6129..54d6478ecd4a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -144,7 +144,7 @@ public class BiometricStateCallbackTest {
@Test
public void testAuthentication_enrollmentCallbackNeverNotified() {
- AuthenticationClient<?> client = mock(AuthenticationClient.class);
+ AuthenticationClient<?, ?> client = mock(AuthenticationClient.class);
mCallback.onClientFinished(client, true /* success */);
verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
anyBoolean());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index a4048a27dfb5..25a700a5275f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -16,24 +16,35 @@
package com.android.server.biometrics.sensors.fingerprint;
+import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.app.AppOpsManager.OP_USE_BIOMETRIC;
+import static android.app.AppOpsManager.OP_USE_FINGERPRINT;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
+
+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.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.common.CommonProps;
-import android.hardware.biometrics.common.SensorStrength;
-import android.hardware.biometrics.fingerprint.FingerprintSensorType;
-import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.SensorLocation;
-import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -44,10 +55,13 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -64,6 +78,8 @@ public class FingerprintServiceTest {
private static final int ID_VIRTUAL = 6;
private static final String NAME_DEFAULT = "default";
private static final String NAME_VIRTUAL = "virtual";
+ private static final List<FingerprintSensorPropertiesInternal> HIDL_AUTHENTICATORS =
+ List.of();
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -74,47 +90,80 @@ public class FingerprintServiceTest {
public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
@Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
private BiometricContext mBiometricContext;
@Mock
private IBiometricService mIBiometricService;
@Mock
- private IFingerprint mIFingerprintDefault;
+ private FingerprintProvider mFingerprintDefault;
@Mock
- private IFingerprint mIFingerprintVirtual;
-
- private final SensorProps mSensorPropsDefault = createProps(ID_DEFAULT,
- SensorStrength.STRONG, FingerprintSensorType.POWER_BUTTON);
- private final SensorProps mSensorPropsVirtual = createProps(ID_VIRTUAL,
- SensorStrength.STRONG, FingerprintSensorType.UNDER_DISPLAY_OPTICAL);
+ private FingerprintProvider mFingerprintVirtual;
+ @Mock
+ private IFingerprintServiceReceiver mServiceReceiver;
+ @Mock
+ private IBinder mToken;
+
+ @Captor
+ private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor;
+
+ private final FingerprintSensorPropertiesInternal mSensorPropsDefault =
+ new FingerprintSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_REAR,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ private final FingerprintSensorPropertiesInternal mSensorPropsVirtual =
+ new FingerprintSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_UDFPS_OPTICAL,
+ false /* resetLockoutRequiresHardwareAuthToken */);
private FingerprintService mService;
@Before
public void setup() throws Exception {
- when(mIFingerprintDefault.getSensorProps()).thenReturn(
- new SensorProps[]{mSensorPropsDefault});
- when(mIFingerprintVirtual.getSensorProps()).thenReturn(
- new SensorProps[]{mSensorPropsVirtual});
-
- mContext.getTestablePermissions().setPermission(
- USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
+ when(mFingerprintDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
+ when(mFingerprintVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+ when(mFingerprintDefault.containsSensor(anyInt()))
+ .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
+ when(mFingerprintVirtual.containsSensor(anyInt()))
+ .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
+
+ mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
+ for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) {
+ when(mAppOpsManager.noteOp(eq(permission), anyInt(), any(), any(), any()))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ for (String permission : List.of(USE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
+ mContext.getTestablePermissions().setPermission(
+ permission, PackageManager.PERMISSION_GRANTED);
+ }
}
private void initServiceWith(String... aidlInstances) {
mService = new FingerprintService(mContext, mBiometricContext,
() -> mIBiometricService,
() -> aidlInstances,
- (fqName) -> {
- if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault;
- if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual;
+ (name) -> {
+ if (NAME_DEFAULT.equals(name)) return mFingerprintDefault;
+ if (NAME_VIRTUAL.equals(name)) return mFingerprintVirtual;
return null;
});
}
+ private void initServiceWithAndWait(String... aidlInstances) throws Exception {
+ initServiceWith(aidlInstances);
+ mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
+ waitForRegistration();
+ }
+
@Test
public void registerAuthenticators_defaultOnly() throws Exception {
initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
- mService.mServiceWrapper.registerAuthenticators(List.of());
+ mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
@@ -126,7 +175,7 @@ public class FingerprintServiceTest {
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
- mService.mServiceWrapper.registerAuthenticators(List.of());
+ mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -136,7 +185,7 @@ public class FingerprintServiceTest {
public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
initServiceWith(NAME_VIRTUAL);
- mService.mServiceWrapper.registerAuthenticators(List.of());
+ mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -155,13 +204,47 @@ public class FingerprintServiceTest {
latch.await(5, TimeUnit.SECONDS);
}
- private static SensorProps createProps(int id, byte strength, byte type) {
- final SensorProps props = new SensorProps();
- props.commonProps = new CommonProps();
- props.commonProps.sensorId = id;
- props.commonProps.sensorStrength = strength;
- props.sensorType = type;
- props.sensorLocations = new SensorLocation[]{new SensorLocation()};
- return props;
+ @Test
+ public void authenticateWithDefaultSensorId() throws Exception {
+ initServiceWithAndWait(NAME_DEFAULT, NAME_VIRTUAL);
+
+ final long operationId = 2;
+ mService.mServiceWrapper.authenticate(mToken, operationId, mServiceReceiver,
+ new FingerprintAuthenticateOptions.Builder()
+ .setSensorId(SENSOR_ID_ANY)
+ .build());
+
+ final FingerprintAuthenticateOptions options =
+ verifyAuthenticateWithNewRequestId(mFingerprintDefault, operationId);
+ assertThat(options.getSensorId()).isEqualTo(ID_DEFAULT);
+ verifyNoAuthenticate(mFingerprintVirtual);
+ }
+
+
+ private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
+ FingerprintProvider provider, long operationId) {
+ return verifyAuthenticateWithNewRequestId(
+ provider, operationId, true /* shouldSchedule */);
+ }
+
+ private void verifyNoAuthenticate(FingerprintProvider provider) {
+ verifyAuthenticateWithNewRequestId(
+ provider, 0 /* operationId */, false /* shouldSchedule */);
+ }
+
+ private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
+ FingerprintProvider provider, long operationId, boolean shouldSchedule) {
+ verify(provider, shouldSchedule ? times(1) : never())
+ .scheduleAuthenticate(eq(mToken), eq(operationId), anyInt(), any(),
+ mAuthenticateOptionsCaptor.capture(), anyBoolean(), anyInt(),
+ anyBoolean());
+ verify(provider, never()).scheduleAuthenticate(eq(mToken), anyLong(),
+ anyInt(), any(), mAuthenticateOptionsCaptor.capture(), anyLong(),
+ anyBoolean(), anyInt(), anyBoolean());
+
+ if (shouldSchedule) {
+ return mAuthenticateOptionsCaptor.getValue();
+ }
+ return null;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 99f7905a9f70..f0f975ccf5ff 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -41,6 +41,7 @@ import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -414,11 +415,16 @@ public class FingerprintAuthenticationClientTest {
when(mHal.getInterfaceVersion()).thenReturn(version);
final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final FingerprintAuthenticateOptions options = new FingerprintAuthenticateOptions.Builder()
+ .setOpPackageName("test-owner")
+ .setUserId(5)
+ .setSensorId(9)
+ .build();
return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
- REQUEST_ID, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
- false /* restricted */, "test-owner", 4 /* cookie */,
+ REQUEST_ID, mClientMonitorCallbackConverter, OP_ID,
+ false /* restricted */, options, 4 /* cookie */,
false /* requireConfirmation */,
- 9 /* sensorId */, mBiometricLogger, mBiometricContext,
+ mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
null /* taskStackListener */, null /* lockoutCache */,
mUdfpsOverlayController, mSideFpsController, null, allowBackgroundAuthentication,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 2dbd8f69d694..e741e446da85 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -117,8 +118,13 @@ public class FingerprintDetectClientTest {
final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
return new FingerprintDetectClient(mContext, () -> aidl, mToken,
- 6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */,
- "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext,
+ 6 /* requestId */, mClientMonitorCallbackConverter,
+ new FingerprintAuthenticateOptions.Builder()
+ .setUserId(2)
+ .setSensorId(1)
+ .setOpPackageName("a-test")
+ .build(),
+ mBiometricLogger, mBiometricContext,
mUdfpsOverlayController, null, true /* isStrongBiometric */);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index f0d8616ece9f..580644347c84 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -164,10 +164,9 @@ public class FingerprintInternalCleanupClientTest {
}
protected FingerprintInternalCleanupClient createClient() {
- final List<Fingerprint> enrollments = new ArrayList<>();
final Map<Integer, Long> authenticatorIds = new HashMap<>();
return new FingerprintInternalCleanupClient(mContext, () -> mAidlSession, 2 /* userId */,
- "the.test.owner", SENSOR_ID, mLogger, mBiometricContext, enrollments,
+ "the.test.owner", SENSOR_ID, mLogger, mBiometricContext,
mFingerprintUtils, authenticatorIds) {
@Override
protected void onAddUnknownTemplate(int userId,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 6431e88b1acb..1259d7189a6d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -81,7 +81,7 @@ public class SensorControllerTest {
@Test
public void createSensor_invalidHandle_throwsException() {
doReturn(/* handle= */0).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
Throwable thrown = assertThrows(
RuntimeException.class,
@@ -138,7 +138,7 @@ public class SensorControllerTest {
private void doCreateSensorSuccessfully() {
doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
assertThat(mSensorController.createSensor(mSensorToken, mVirtualSensorConfig))
.isEqualTo(SENSOR_HANDLE);
}
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 0cd50f03fbfa..8f4c81f26408 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
@@ -16,11 +16,11 @@
package com.android.server.companion.virtual;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.Context.DEVICE_ID_INVALID;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
@@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -67,7 +68,11 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.hardware.Sensor;
+import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IDisplayManager;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.IInputManager;
import android.hardware.input.VirtualDpadConfig;
import android.hardware.input.VirtualKeyEvent;
@@ -144,6 +149,7 @@ public class VirtualDeviceManagerServiceTest {
private static final String DEVICE_NAME_3 = "device name 3";
private static final int DISPLAY_ID_1 = 2;
private static final int DISPLAY_ID_2 = 3;
+ private static final int NON_EXISTENT_DISPLAY_ID = 42;
private static final int DEVICE_OWNER_UID_1 = 50;
private static final int DEVICE_OWNER_UID_2 = 51;
private static final int UID_1 = 0;
@@ -162,6 +168,8 @@ public class VirtualDeviceManagerServiceTest {
private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
private static final int VIRTUAL_DEVICE_ID_1 = 42;
private static final int VIRTUAL_DEVICE_ID_2 = 43;
+ private static final VirtualDisplayConfig VIRTUAL_DISPLAY_CONFIG =
+ new VirtualDisplayConfig.Builder("virtual_display", 640, 480, 400).build();
private static final VirtualDpadConfig DPAD_CONFIG =
new VirtualDpadConfig.Builder()
.setVendorId(VENDOR_ID)
@@ -221,6 +229,8 @@ public class VirtualDeviceManagerServiceTest {
@Mock
private DisplayManagerInternal mDisplayManagerInternalMock;
@Mock
+ private IDisplayManager mIDisplayManager;
+ @Mock
private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
@Mock
private DevicePolicyManager mDevicePolicyManagerMock;
@@ -237,6 +247,8 @@ public class VirtualDeviceManagerServiceTest {
@Mock
private IVirtualDeviceSoundEffectListener mSoundEffectListener;
@Mock
+ private IVirtualDisplayCallback mVirtualDisplayCallback;
+ @Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
@@ -271,9 +283,13 @@ public class VirtualDeviceManagerServiceTest {
private Intent createRestrictedActivityBlockedIntent(List displayCategories,
String targetDisplayCategory) {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(displayCategories), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(),
+ eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1);
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("display", 640, 480,
+ 420).setDisplayCategories(displayCategories).build();
+ mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback,
+ NONBLOCKED_APP_PACKAGE_NAME);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -327,6 +343,7 @@ public class VirtualDeviceManagerServiceTest {
mContext = Mockito.spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt());
+ doNothing().when(mContext).sendBroadcastAsUser(any(), any());
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
mDevicePolicyManagerMock);
@@ -369,15 +386,13 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
- mDeviceImpl.mVirtualDisplayIds.remove(DISPLAY_ID_1);
-
- assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
+ assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
.isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_withValidVirtualDisplayId_returnsDeviceId() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
.isEqualTo(mDeviceImpl.getDeviceId());
@@ -485,7 +500,7 @@ public class VirtualDeviceManagerServiceTest {
.build();
doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
- anyInt(), anyInt(), anyString(), anyString(), any());
+ anyInt(), anyInt(), anyString(), anyString(), anyInt(), any());
mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE);
@@ -503,10 +518,9 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDeviceIdsForUid_differentUidOnDevice_returnsNull() {
- GenericWindowPolicyController gwpc =
- mDeviceImpl.createWindowPolicyController(new ArrayList<>());
- mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
- gwpc.onRunningAppsChanged(Sets.newArraySet(UID_2));
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+ Sets.newArraySet(UID_2));
Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
assertThat(deviceIds).isEmpty();
@@ -514,10 +528,9 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDeviceIdsForUid_oneUidOnDevice_returnsCorrectId() {
- GenericWindowPolicyController gwpc =
- mDeviceImpl.createWindowPolicyController(new ArrayList<>());
- mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
- gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1));
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+ Sets.newArraySet(UID_1));
Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
@@ -525,10 +538,10 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDeviceIdsForUid_twoUidsOnDevice_returnsCorrectId() {
- GenericWindowPolicyController gwpc =
- mDeviceImpl.createWindowPolicyController(new ArrayList<>());
- mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
- gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+
+ mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+ Sets.newArraySet(UID_1, UID_2));
Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
@@ -538,11 +551,10 @@ public class VirtualDeviceManagerServiceTest {
public void getDeviceIdsForUid_twoDevicesUidOnOne_returnsCorrectId() {
VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
DEVICE_OWNER_UID_2);
+ addVirtualDisplay(secondDevice, DISPLAY_ID_2);
- GenericWindowPolicyController gwpc =
- secondDevice.createWindowPolicyController(new ArrayList<>());
- secondDevice.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_2);
- gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1));
+ secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
+ Sets.newArraySet(UID_1));
Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
assertThat(deviceIds).containsExactly(secondDevice.getDeviceId());
@@ -550,16 +562,16 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDeviceIdsForUid_twoDevicesUidOnBoth_returnsCorrectId() {
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
DEVICE_OWNER_UID_2);
- GenericWindowPolicyController gwpc1 =
- mDeviceImpl.createWindowPolicyController(new ArrayList<>());
- GenericWindowPolicyController gwpc2 =
- secondDevice.createWindowPolicyController(new ArrayList<>());
- mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc1, DISPLAY_ID_1);
- secondDevice.onVirtualDisplayCreatedLocked(gwpc2, DISPLAY_ID_2);
- gwpc1.onRunningAppsChanged(Sets.newArraySet(UID_1));
- gwpc2.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
+ addVirtualDisplay(secondDevice, DISPLAY_ID_2);
+
+
+ mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+ Sets.newArraySet(UID_1));
+ secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
+ Sets.newArraySet(UID_1, UID_2));
Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
assertThat(deviceIds).containsExactly(
@@ -568,8 +580,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getPreferredLocaleListForApp_keyboardAttached_returnLocaleHints() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
-
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
@@ -609,8 +620,8 @@ public class VirtualDeviceManagerServiceTest {
.setLanguageTag("fr-FR")
.build();
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
- secondDevice.mVirtualDisplayIds.add(DISPLAY_ID_2);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ addVirtualDisplay(secondDevice, DISPLAY_ID_2);
mDeviceImpl.createVirtualKeyboard(firstKeyboardConfig, BINDER);
secondDevice.createVirtualKeyboard(secondKeyboardConfig, secondBinder);
@@ -640,10 +651,9 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
// This call should not throw any exceptions.
- mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+ mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1);
}
@Test
@@ -659,8 +669,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onVirtualDisplayRemovedLocked_listenersNotified() {
mLocalService.registerVirtualDisplayListener(mDisplayListener);
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
TestableLooper.get(this).processAllMessages();
@@ -723,8 +733,7 @@ public class VirtualDeviceManagerServiceTest {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), anyInt(), eq(null));
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), eq(DISPLAY_ID_1), eq(null));
@@ -733,12 +742,9 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
throws RemoteException {
- GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController(
- new ArrayList<>());
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
assertThrows(IllegalStateException.class,
- () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1));
+ () -> addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1));
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
@@ -749,13 +755,12 @@ public class VirtualDeviceManagerServiceTest {
public void onVirtualDisplayRemovedLocked_unknownDisplayId_throwsException() {
final int unknownDisplayId = 999;
assertThrows(IllegalStateException.class,
- () -> mDeviceImpl.onVirtualDisplayRemovedLocked(unknownDisplayId));
+ () -> mDeviceImpl.onVirtualDisplayRemoved(unknownDisplayId));
}
@Test
public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -764,14 +769,13 @@ public class VirtualDeviceManagerServiceTest {
nullable(String.class), eq(DISPLAY_ID_1), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
- mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+ mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1);
verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
}
@Test
public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -825,7 +829,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualTouchscreen_positiveDisplayDimension_successful() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
VirtualTouchscreenConfig positiveConfig =
new VirtualTouchscreenConfig.Builder(
/* touchscrenWidth= */ 600, /* touchscreenHeight= */ 800)
@@ -863,7 +867,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualNavigationTouchpad_positiveDisplayDimension_successful() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
VirtualNavigationTouchpadConfig positiveConfig =
new VirtualNavigationTouchpadConfig.Builder(
/* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
@@ -888,7 +892,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualDpad_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER));
@@ -897,7 +901,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualKeyboard_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER));
@@ -906,7 +910,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualMouse_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER));
@@ -915,7 +919,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualTouchscreen_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER));
@@ -924,7 +928,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualNavigationTouchpad_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG,
@@ -934,7 +938,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onAudioSessionStarting_noPermission_failsSecurityException() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
assertThrows(SecurityException.class,
() -> mDeviceImpl.onAudioSessionStarting(
@@ -951,7 +955,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualDpad_hasDisplay_obtainFileDescriptor() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER);
assertWithMessage("Virtual dpad should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -961,7 +965,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -971,7 +975,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualKeyboard_keyboardCreated_localeUpdated() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.getInputDeviceDescriptors())
@@ -992,7 +996,7 @@ public class VirtualDeviceManagerServiceTest {
.setAssociatedDisplayId(DISPLAY_ID_1)
.build();
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualKeyboard(configWithoutExplicitLayoutInfo, BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
.that(mInputController.getInputDeviceDescriptors())
@@ -1005,7 +1009,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void virtualDeviceWithoutKeyboard_noLocaleUpdate() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
// no preceding call to createVirtualKeyboard()
assertThat(mDeviceImpl.getDeviceLocaleList()).isNull();
@@ -1013,7 +1017,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER);
assertWithMessage("Virtual mouse should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1023,7 +1027,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER);
assertWithMessage("Virtual touchscreen should register fd when the display matches").that(
mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1033,7 +1037,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void createVirtualNavigationTouchpad_hasDisplay_obtainFileDescriptor() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, BINDER);
assertWithMessage("Virtual navigation touchpad should register fd when the display matches")
.that(
@@ -1055,8 +1059,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onAudioSessionStarting_hasVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
@@ -1065,8 +1068,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void onAudioSessionEnded_noVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
mDeviceImpl.onAudioSessionEnded();
@@ -1076,8 +1078,7 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void close_cleanVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
mDeviceImpl.close();
@@ -1321,9 +1322,9 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void setShowPointerIcon_setsValueForAllDisplays() {
- mDeviceImpl.mVirtualDisplayIds.add(1);
- mDeviceImpl.mVirtualDisplayIds.add(2);
- mDeviceImpl.mVirtualDisplayIds.add(3);
+ addVirtualDisplay(mDeviceImpl, 1);
+ addVirtualDisplay(mDeviceImpl, 2);
+ addVirtualDisplay(mDeviceImpl, 3);
VirtualMouseConfig config1 = new VirtualMouseConfig.Builder()
.setAssociatedDisplayId(1)
.setInputDeviceName(DEVICE_NAME_1)
@@ -1346,7 +1347,9 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createVirtualMouse(config1, BINDER);
mDeviceImpl.createVirtualMouse(config2, BINDER);
mDeviceImpl.createVirtualMouse(config3, BINDER);
+ clearInvocations(mInputManagerInternalMock);
mDeviceImpl.setShowPointerIcon(false);
+
verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt());
verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt());
mDeviceImpl.setShowPointerIcon(true);
@@ -1355,9 +1358,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1376,9 +1378,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1397,9 +1398,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1418,9 +1418,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openVendingOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1439,9 +1438,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1460,9 +1458,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -1482,9 +1479,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
gwpc.onRunningAppsChanged(uids);
@@ -1497,11 +1493,10 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
- mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+ gwpc.unregisterRunningAppsChangedListener(mDeviceImpl);
// This call should not throw any exceptions.
gwpc.onRunningAppsChanged(uids);
@@ -1512,9 +1507,8 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void canActivityBeLaunched_activityCanLaunch() {
Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
@@ -1537,9 +1531,8 @@ public class VirtualDeviceManagerServiceTest {
doReturn(interceptor).when(interceptor).asBinder();
doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
@@ -1581,9 +1574,8 @@ public class VirtualDeviceManagerServiceTest {
doReturn(interceptor).when(interceptor).asBinder();
doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
- mDeviceImpl.onVirtualDisplayCreatedLocked(
- mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
- GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
DISPLAY_ID_1);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
@@ -1626,8 +1618,7 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
- public void
- restrictedActivityOnNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
+ public void restrictedActivityNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "def");
verify(mContext).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
@@ -1654,15 +1645,15 @@ public class VirtualDeviceManagerServiceTest {
@Test
public void getDisplayIdsForDevice_oneDisplay_resultContainsCorrectDisplayId() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
assertThat(displayIds).containsExactly(DISPLAY_ID_1);
}
@Test
public void getDisplayIdsForDevice_twoDisplays_resultContainsCorrectDisplayIds() {
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
- mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_2);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_2);
ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
assertThat(displayIds).containsExactly(DISPLAY_ID_1, DISPLAY_ID_2);
}
@@ -1677,15 +1668,22 @@ public class VirtualDeviceManagerServiceTest {
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
VirtualDeviceParams params) {
VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
- mAssociationInfo, new Binder(), ownerUid, virtualDeviceId,
- mInputController, mSensorController, mCameraAccessController,
- /* onDeviceCloseListener= */ deviceId -> mVdms.removeVirtualDevice(deviceId),
+ mAssociationInfo, mVdms, new Binder(), ownerUid, virtualDeviceId,
+ mInputController, mSensorController, mCameraAccessController
+ /* onDeviceCloseListener= */ /*deviceId -> mVdms.removeVirtualDevice(deviceId)*/,
mPendingTrampolineCallback, mActivityListener, mSoundEffectListener,
- mRunningAppsChangedCallback, params);
+ mRunningAppsChangedCallback, params, new DisplayManagerGlobal(mIDisplayManager));
mVdms.addVirtualDevice(virtualDeviceImpl);
return virtualDeviceImpl;
}
+ private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId) {
+ when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback),
+ eq(virtualDevice), any(), any())).thenReturn(displayId);
+ virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback,
+ NONBLOCKED_APP_PACKAGE_NAME);
+ }
+
/** Helper class to drop permissions temporarily and restore them at the end of a test. */
static final class DropShellPermissionsTemporarily implements AutoCloseable {
DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
index bb28a3689f68..a2e204d89424 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
@@ -16,8 +16,8 @@
package com.android.server.companion.virtual;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
-import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.Context.DEVICE_ID_INVALID;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 800f60bec828..ffe2fec380a8 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -43,6 +43,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.server.display.BrightnessThrottler.Injector;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.mode.DisplayModeDirectorTest;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 6def7b1c8c35..8981160d1f25 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -176,21 +176,18 @@ public class BrightnessTrackerTest {
assertFalse(mInjector.mColorSamplingEnabled);
// Update brightness config to enabled color sampling.
- mTracker.setBrightnessConfiguration(buildBrightnessConfiguration(
- /* collectColorSamples= */ true));
+ mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true);
mInjector.waitForHandler();
assertTrue(mInjector.mColorSamplingEnabled);
// Update brightness config to disable color sampling.
- mTracker.setBrightnessConfiguration(buildBrightnessConfiguration(
- /* collectColorSamples= */ false));
+ mTracker.setShouldCollectColorSample(/* collectColorSamples= */ false);
mInjector.waitForHandler();
assertFalse(mInjector.mColorSamplingEnabled);
// Pretend screen is off, update config to turn on color sampling.
mInjector.sendScreenChange(/* screenOn= */ false);
- mTracker.setBrightnessConfiguration(buildBrightnessConfiguration(
- /* collectColorSamples= */ true));
+ mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true);
mInjector.waitForHandler();
assertFalse(mInjector.mColorSamplingEnabled);
@@ -883,7 +880,7 @@ public class BrightnessTrackerTest {
private void startTracker(BrightnessTracker tracker, float initialBrightness,
boolean collectColorSamples) {
tracker.start(initialBrightness);
- tracker.setBrightnessConfiguration(buildBrightnessConfiguration(collectColorSamples));
+ tracker.setShouldCollectColorSample(collectColorSamples);
mInjector.waitForHandler();
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 7e6eeee9e713..f256c8a17c56 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.display;
+package com.android.server.display.mode;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
@@ -27,8 +27,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
-import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE;
-import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
+import static com.android.server.display.mode.DisplayModeDirector.Vote.INVALID_SIZE;
import static com.google.common.truth.Truth.assertThat;
@@ -88,9 +87,11 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
-import com.android.server.display.DisplayModeDirector.BrightnessObserver;
-import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
-import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.TestUtils;
+import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
+import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
+import com.android.server.display.mode.DisplayModeDirector.Vote;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -126,6 +127,8 @@ public class DisplayModeDirectorTest {
private static final int DISPLAY_ID = 0;
private static final float TRANSITION_POINT = 0.763f;
+ private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
+
private Context mContext;
private FakesInjector mInjector;
private Handler mHandler;
@@ -2234,7 +2237,7 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(IThermalEventListener.class);
verify(mThermalServiceMock).registerThermalEventListenerWithType(
- thermalEventListener.capture(), eq(Temperature.TYPE_SKIN));
+ thermalEventListener.capture(), eq(Temperature.TYPE_SKIN));
final IThermalEventListener listener = thermalEventListener.getValue();
// Verify that there is no skin temperature vote initially.
@@ -2548,7 +2551,7 @@ public class DisplayModeDirectorTest {
KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
}
- void setBrightnessThrottlingData(String brightnessThrottlingData) {
+ public void setBrightnessThrottlingData(String brightnessThrottlingData) {
putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_BRIGHTNESS_THROTTLING_DATA, brightnessThrottlingData);
}
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
index 23d708227e4f..be1375310704 100644
--- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -16,6 +16,9 @@
package com.android.server.job;
+import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
+import static android.app.job.JobInfo.NETWORK_TYPE_NONE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -525,7 +528,9 @@ public class PendingJobQueueTest {
final boolean ui = random.nextBoolean();
final boolean ej = !ui && random.nextBoolean();
JobStatus job = createJobStatus("testPendingJobSorting_Random",
- createJobInfo(i).setExpedited(ej).setUserInitiated(ui), random.nextInt(250));
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui)
+ .setRequiredNetworkType(ui ? NETWORK_TYPE_ANY : NETWORK_TYPE_NONE),
+ random.nextInt(250));
job.enqueueTime = random.nextInt(1_000_000);
jobQueue.add(job);
}
@@ -562,7 +567,9 @@ public class PendingJobQueueTest {
final boolean ui = random.nextBoolean();
final boolean ej = !ui && random.nextBoolean();
JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
- createJobInfo(i).setExpedited(ej).setUserInitiated(ui), random.nextInt(50));
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui)
+ .setRequiredNetworkType(ui ? NETWORK_TYPE_ANY : NETWORK_TYPE_NONE),
+ random.nextInt(50));
job.enqueueTime = random.nextInt(1_000_000);
job.overrideState = random.nextInt(4);
jobQueue.add(job);
@@ -586,7 +593,8 @@ public class PendingJobQueueTest {
final boolean ui = random.nextFloat() < .02;
final boolean ej = !ui && random.nextFloat() < .03;
JobStatus job = createJobStatus("testPendingJobSortingTransitivity_Concentrated",
- createJobInfo(i).setExpedited(ej).setUserInitiated(ui),
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui)
+ .setRequiredNetworkType(ui ? NETWORK_TYPE_ANY : NETWORK_TYPE_NONE),
random.nextInt(20));
job.enqueueTime = random.nextInt(250);
job.overrideState = random.nextFloat() < .01
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 9686c385e91e..1c33d0de4568 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -165,10 +165,11 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
- protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
+ protected void tieProfileLockToParent(int profileUserId, int parentUserId,
+ LockscreenCredential password) {
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(password, 0);
- mStorage.writeChildProfileLock(userId, parcel.marshall());
+ mStorage.writeChildProfileLock(profileUserId, parcel.marshall());
parcel.recycle();
}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
new file mode 100644
index 000000000000..24ed42cab63a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.annotation.IdRes;
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.server.audio.AudioService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class DeviceRouteControllerTest {
+
+ private static final String DEFAULT_ROUTE_NAME = "default_route";
+ private static final String DEFAULT_HEADPHONES_NAME = "headphone";
+ private static final String DEFAULT_HEADSET_NAME = "headset";
+ private static final String DEFAULT_DOCK_NAME = "dock";
+ private static final String DEFAULT_HDMI_NAME = "hdmi";
+ private static final String DEFAULT_USB_NAME = "usb";
+ private static final int VOLUME_DEFAULT_VALUE = 0;
+ private static final int VOLUME_VALUE_SAMPLE_1 = 10;
+
+ private static AudioRoutesInfo createFakeBluetoothAudioRoute() {
+ AudioRoutesInfo btRouteInfo = new AudioRoutesInfo();
+ btRouteInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
+ btRouteInfo.bluetoothName = "bt_device";
+ return btRouteInfo;
+ }
+
+ @RunWith(JUnit4.class)
+ public static class DefaultDeviceRouteValueTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private AudioService mAudioService;
+ @Mock
+ private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void initialize_noRoutesInfo_defaultRouteIsNotNull() {
+ // Mocking default_audio_route_name.
+ when(mResources.getText(R.string.default_audio_route_name))
+ .thenReturn(DEFAULT_ROUTE_NAME);
+
+ // Default route should be initialized even when AudioService returns null.
+ when(mAudioService.startWatchingRoutes(any())).thenReturn(null);
+
+ DeviceRouteController deviceRouteController = new DeviceRouteController(
+ mContext,
+ mAudioManager,
+ mAudioService,
+ mOnDeviceRouteChangedListener
+ );
+
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+
+ assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
+ .isTrue();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+ }
+
+ @Test
+ public void initialize_bluetoothRouteAvailable_deviceRouteReturnsDefaultRoute() {
+ // Mocking default_audio_route_name.
+ when(mResources.getText(R.string.default_audio_route_name))
+ .thenReturn(DEFAULT_ROUTE_NAME);
+
+ // This route should be ignored.
+ AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
+ when(mAudioService.startWatchingRoutes(any())).thenReturn(fakeBluetoothAudioRoute);
+
+ DeviceRouteController deviceRouteController = new DeviceRouteController(
+ mContext,
+ mAudioManager,
+ mAudioService,
+ mOnDeviceRouteChangedListener
+ );
+
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+
+ assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
+ .isTrue();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+ }
+ }
+
+ @RunWith(Parameterized.class)
+ public static class DeviceRouteInitializationTest {
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ { /* expected res */
+ com.android.internal.R.string.default_audio_route_name_headphones,
+ /* expected name */
+ DEFAULT_HEADPHONES_NAME,
+ /* expected type */
+ MediaRoute2Info.TYPE_WIRED_HEADPHONES,
+ /* actual audio route type */
+ AudioRoutesInfo.MAIN_HEADPHONES },
+ { /* expected res */
+ com.android.internal.R.string.default_audio_route_name_headphones,
+ /* expected name */
+ DEFAULT_HEADSET_NAME,
+ /* expected type */
+ MediaRoute2Info.TYPE_WIRED_HEADSET,
+ /* actual audio route type */
+ AudioRoutesInfo.MAIN_HEADSET },
+ { /* expected res */
+ R.string.default_audio_route_name_dock_speakers,
+ /* expected name */
+ DEFAULT_DOCK_NAME,
+ /* expected type */
+ MediaRoute2Info.TYPE_DOCK,
+ /* actual audio route type */
+ AudioRoutesInfo.MAIN_DOCK_SPEAKERS },
+ { /* expected res */
+ R.string.default_audio_route_name_external_device,
+ /* expected name */
+ DEFAULT_HDMI_NAME,
+ /* expected type */
+ MediaRoute2Info.TYPE_HDMI,
+ /* actual audio route type */
+ AudioRoutesInfo.MAIN_HDMI },
+ { /* expected res */
+ R.string.default_audio_route_name_usb,
+ /* expected name */
+ DEFAULT_USB_NAME,
+ /* expected type */
+ MediaRoute2Info.TYPE_USB_DEVICE,
+ /* actual audio route type */
+ AudioRoutesInfo.MAIN_USB }
+ });
+ }
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private AudioService mAudioService;
+ @Mock
+ private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+
+ @IdRes
+ private final int mExpectedRouteNameResource;
+ private final String mExpectedRouteNameValue;
+ private final int mExpectedRouteType;
+ private final int mActualAudioRouteType;
+
+ public DeviceRouteInitializationTest(int expectedRouteNameResource,
+ String expectedRouteNameValue,
+ int expectedMediaRouteType,
+ int actualAudioRouteType) {
+ this.mExpectedRouteNameResource = expectedRouteNameResource;
+ this.mExpectedRouteNameValue = expectedRouteNameValue;
+ this.mExpectedRouteType = expectedMediaRouteType;
+ this.mActualAudioRouteType = actualAudioRouteType;
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void initialize_wiredRouteAvailable_deviceRouteReturnsWiredRoute() {
+ // Mocking default_audio_route_name.
+ when(mResources.getText(R.string.default_audio_route_name))
+ .thenReturn(DEFAULT_ROUTE_NAME);
+
+ // At first, WiredRouteController should initialize device
+ // route based on AudioService response.
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = mActualAudioRouteType;
+ when(mAudioService.startWatchingRoutes(any())).thenReturn(audioRoutesInfo);
+
+ when(mResources.getText(mExpectedRouteNameResource))
+ .thenReturn(mExpectedRouteNameValue);
+
+ DeviceRouteController deviceRouteController = new DeviceRouteController(
+ mContext,
+ mAudioManager,
+ mAudioService,
+ mOnDeviceRouteChangedListener
+ );
+
+ MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute();
+
+ assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType);
+ assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue))
+ .isTrue();
+ // Volume did not change, so it should be set to default value (0).
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+ }
+ }
+
+ @RunWith(JUnit4.class)
+ public static class VolumeAndDeviceRoutesChangesTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private AudioService mAudioService;
+ @Mock
+ private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+
+ @Captor
+ private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
+
+ private DeviceRouteController mDeviceRouteController;
+ private IAudioRoutesObserver.Stub mAudioRoutesObserver;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+
+ when(mResources.getText(R.string.default_audio_route_name))
+ .thenReturn(DEFAULT_ROUTE_NAME);
+
+ // Setting built-in speaker as default speaker.
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
+ when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
+ .thenReturn(audioRoutesInfo);
+
+ mDeviceRouteController = new DeviceRouteController(
+ mContext,
+ mAudioManager,
+ mAudioService,
+ mOnDeviceRouteChangedListener
+ );
+
+ mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue();
+ }
+
+ @Test
+ public void newDeviceConnects_wiredDevice_deviceRouteReturnsWiredDevice() {
+ // Connecting wired headset
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+
+ when(mResources.getText(
+ com.android.internal.R.string.default_audio_route_name_headphones))
+ .thenReturn(DEFAULT_HEADPHONES_NAME);
+
+ // Simulating wired device being connected.
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+
+ assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME))
+ .isTrue();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+ }
+
+ @Test
+ public void newDeviceConnects_bluetoothDevice_deviceRouteReturnsBluetoothDevice() {
+ // Simulating bluetooth speaker being connected.
+ AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
+ callAudioRoutesObserver(fakeBluetoothAudioRoute);
+
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+
+ assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME))
+ .isTrue();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+ }
+
+ @Test
+ public void updateVolume_differentValue_updatesDeviceRouteVolume() {
+ MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE);
+
+ assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue();
+
+ actualMediaRoute = mDeviceRouteController.getDeviceRoute();
+ assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1);
+ }
+
+ @Test
+ public void updateVolume_sameValue_returnsFalse() {
+ assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue();
+ assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isFalse();
+ }
+
+ /**
+ * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)}
+ * from {@link AudioService}. This happens when there is a wired route change,
+ * like a wired headset being connected.
+ *
+ * @param audioRoutesInfo updated state of connected wired device
+ */
+ private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) {
+ try {
+ // this is a captured observer implementation
+ // from WiredRoutesController's AudioService#startWatchingRoutes call
+ mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo);
+ } catch (RemoteException exception) {
+ // Should not happen since the object is mocked.
+ assertWithMessage("An unexpected RemoteException happened.").fail();
+ }
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index b20c63ca504f..0a718e329498 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -118,10 +118,12 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -4005,6 +4007,156 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
+ public void testSaveCorruptAndLoadUser() throws Exception {
+ // First, create some shortcuts and save.
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title1-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title1-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_16x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title2-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title2-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ mRunningUsers.put(USER_10, true);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.icon2));
+
+ final ShortcutInfo si1 = makeShortcut(
+ "s1",
+ "title10-1-1",
+ makeComponent(ShortcutActivity.class),
+ icon1,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+ "key1", "val1", "nest", makeBundle("key", 123)),
+ /* weight */ 10);
+
+ final ShortcutInfo si2 = makeShortcut(
+ "s2",
+ "title10-1-2",
+ /* activity */ null,
+ icon2,
+ makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+ /* weight */ 12);
+
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ // Save and corrupt the primary files.
+ mService.saveDirtyInfo();
+ try (Writer os = new FileWriter(mService.getUserFile(UserHandle.USER_SYSTEM))) {
+ os.write("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<user locales=\"en\" last-app-scan-time2=\"14400000");
+ }
+ try (Writer os = new FileWriter(mService.getUserFile(USER_10))) {
+ os.write("<?xml version='1.0' encoding='utf");
+ }
+
+ // Restore.
+ initService();
+
+ // Before the load, the map should be empty.
+ assertEquals(0, mService.getShortcutsForTest().size());
+
+ // this will pre-load the per-user info.
+ mService.handleUnlockUser(UserHandle.USER_SYSTEM);
+
+ // Now it's loaded.
+ assertEquals(1, mService.getShortcutsForTest().size());
+
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title1-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title1-2", getCallerShortcut("s2").getTitle());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title2-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title2-2", getCallerShortcut("s2").getTitle());
+ });
+
+ // Start another user
+ mService.handleUnlockUser(USER_10);
+
+ // Now the size is 2.
+ assertEquals(2, mService.getShortcutsForTest().size());
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertShortcutIds(assertAllDynamic(assertAllHaveIntents(assertAllHaveIcon(
+ mManager.getDynamicShortcuts()))), "s1", "s2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ assertEquals("title10-1-1", getCallerShortcut("s1").getTitle());
+ assertEquals("title10-1-2", getCallerShortcut("s2").getTitle());
+ });
+
+ // Try stopping the user
+ mService.handleStopUser(USER_10);
+
+ // Now it's unloaded.
+ assertEquals(1, mService.getShortcutsForTest().size());
+
+ // TODO Check all other fields
+ }
+
public void testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index f8033557f0f0..d78ab8677800 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -231,6 +231,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
setExternalStatsSyncLocked(mExternalStatsSync);
}
+ @Override
+ public void writeSyncLocked() {
+ }
+
public static class DummyExternalStatsSync implements ExternalStatsSync {
public int flags = 0;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 5a0867f8f584..daa682342836 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -386,16 +386,16 @@ public class TimeDetectorServiceTest {
.when(mMockContext).enforceCallingPermission(anyString(), any());
assertThrows(SecurityException.class,
- () -> mTimeDetectorService.clearNetworkTime());
+ () -> mTimeDetectorService.clearLatestNetworkTime());
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
}
@Test
- public void testClearNetworkTime() throws Exception {
+ public void testClearLatestNetworkSuggestion() throws Exception {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
- mTimeDetectorService.clearNetworkTime();
+ mTimeDetectorService.clearLatestNetworkTime();
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
@@ -403,53 +403,48 @@ public class TimeDetectorServiceTest {
}
@Test
- public void testLatestNetworkTime() {
- NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
- 1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
- when(mMockNtpTrustedTime.getCachedTimeResult())
- .thenReturn(latestNetworkTime);
- UnixEpochTime expected = new UnixEpochTime(
- latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
- assertEquals(expected, mTimeDetectorService.latestNetworkTime());
+ public void testGetLatestNetworkSuggestion() {
+ NetworkTimeSuggestion latestNetworkSuggestion = createNetworkTimeSuggestion();
+ mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkSuggestion);
+
+ assertEquals(latestNetworkSuggestion, mTimeDetectorService.getLatestNetworkSuggestion());
}
@Test
- public void testLatestNetworkTime_noTimeAvailable() {
- when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
- assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime());
+ public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+ mFakeTimeDetectorStrategySpy.setLatestNetworkTime(null);
+
+ assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
}
@Test
- public void testGetLatestNetworkSuggestion() {
+ public void testLatestNetworkTime() {
if (TimeDetectorNetworkTimeHelper.isInUse()) {
- NetworkTimeSuggestion latestNetworkTime = createNetworkTimeSuggestion();
- mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkTime);
+ NetworkTimeSuggestion latestNetworkSuggestion = createNetworkTimeSuggestion();
+ mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkSuggestion);
- assertEquals(latestNetworkTime, mTimeDetectorService.getLatestNetworkSuggestion());
+ assertEquals(latestNetworkSuggestion.getUnixEpochTime(),
+ mTimeDetectorService.latestNetworkTime());
} else {
NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
when(mMockNtpTrustedTime.getCachedTimeResult())
.thenReturn(latestNetworkTime);
- UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
+ UnixEpochTime expected = new UnixEpochTime(
latestNetworkTime.getElapsedRealtimeMillis(),
latestNetworkTime.getTimeMillis());
- NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
- expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
- assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
+ assertEquals(expected, mTimeDetectorService.latestNetworkTime());
}
}
@Test
- public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+ public void testLatestNetworkTime_noTimeAvailable() {
if (TimeDetectorNetworkTimeHelper.isInUse()) {
mFakeTimeDetectorStrategySpy.setLatestNetworkTime(null);
-
- assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
} else {
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
- assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
}
+ assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime());
}
@Test
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index b921838e0bfc..4c0361d30d67 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -263,7 +263,7 @@ public class ShortcutManagerTestUtils {
+ instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+ packageName + " 0");
waitUntil("Failed to get shortcut access",
- () -> hasShortcutAccess(instrumentation, packageName), 20);
+ () -> hasShortcutAccess(instrumentation, packageName), 60);
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 3ecbbfeed254..8f0a5e6f5d0d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -141,9 +141,9 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
*/
@Test
public void testMetaN() throws RemoteException {
- mPhoneWindowManager.overrideExpandNotificationsPanel();
+ mPhoneWindowManager.overrideTogglePanel();
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_N}, 0);
- mPhoneWindowManager.assertExpandNotification();
+ mPhoneWindowManager.assertTogglePanel();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6da9d0c606cf..b6939747a7b6 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -67,6 +67,7 @@ import android.os.RemoteException;
import android.os.Vibrator;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
+import android.util.FeatureFlagUtils;
import android.view.Display;
import android.view.KeyEvent;
import android.view.autofill.AutofillManagerInternal;
@@ -76,6 +77,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -114,6 +116,7 @@ class TestPhoneWindowManager {
@Mock private Vibrator mVibrator;
@Mock private PowerManager mPowerManager;
@Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl;
+ @Mock private InputMethodManagerInternal mInputMethodManagerInternal;
@Mock private AudioManagerInternal mAudioManagerInternal;
@Mock private SearchManager mSearchManager;
@@ -184,6 +187,8 @@ class TestPhoneWindowManager {
() -> LocalServices.getService(eq(GestureLauncherService.class)));
doReturn(null).when(() -> LocalServices.getService(eq(VrManagerInternal.class)));
doReturn(null).when(() -> LocalServices.getService(eq(AutofillManagerInternal.class)));
+ LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
+ LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
@@ -242,6 +247,7 @@ class TestPhoneWindowManager {
void tearDown() {
mHandlerThread.quitSafely();
+ LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
mMockitoSession.finishMocking();
}
@@ -322,12 +328,12 @@ class TestPhoneWindowManager {
doReturn(true).when(mTelecomManager).endCall();
}
- void overrideExpandNotificationsPanel() {
+ void overrideTogglePanel() {
// Can't directly mock on IStatusbarService, use spyOn and override the specific api.
mPhoneWindowManager.getStatusBarService();
spyOn(mPhoneWindowManager.mStatusBarService);
try {
- doNothing().when(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel();
+ doNothing().when(mPhoneWindowManager.mStatusBarService).togglePanel();
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -417,7 +423,13 @@ class TestPhoneWindowManager {
void assertSwitchKeyboardLayout(int direction) {
waitForIdle();
- verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
+ verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction));
+ verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt());
+ } else {
+ verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
+ verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt());
+ }
}
void assertTakeBugreport() {
@@ -428,9 +440,9 @@ class TestPhoneWindowManager {
Assert.assertTrue(intentCaptor.getValue().getAction() == Intent.ACTION_BUG_REPORT);
}
- void assertExpandNotification() throws RemoteException {
+ void assertTogglePanel() throws RemoteException {
waitForIdle();
- verify(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel();
+ verify(mPhoneWindowManager.mStatusBarService).togglePanel();
}
void assertToggleShortcutsMenu() {
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 d7e4c5523eee..169586e2d9dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1023,7 +1023,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
RunningTaskInfo mInfo;
@Override
- public void addStartingWindow(StartingWindowInfo info) { }
+ public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
@Override
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index fafe90a5df55..323894ca76df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1583,10 +1583,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
@Override
- public void addStartingWindow(StartingWindowInfo info) {
+ public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
synchronized (mWMService.mGlobalLock) {
final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
- info.appToken);
+ appToken);
IWindow iWindow = mock(IWindow.class);
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
final WindowState window = WindowTestsBase.createWindow(null,
@@ -1596,8 +1596,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
iWindow,
mPowerManagerWrapper);
activity.mStartingWindow = window;
- mAppWindowMap.put(info.appToken, window);
- mTaskAppMap.put(info.taskInfo.taskId, info.appToken);
+ mAppWindowMap.put(appToken, window);
+ mTaskAppMap.put(info.taskInfo.taskId, appToken);
}
if (mRunnableWhenAddingSplashScreen != null) {
mRunnableWhenAddingSplashScreen.run();
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 0dcf8ce96192..b1cd99420737 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -19,7 +19,6 @@ package com.android.server.usb;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
@@ -35,9 +34,12 @@ import android.os.Bundle;
import android.service.usb.UsbDirectMidiDeviceProto;
import android.util.Log;
+import com.android.internal.midi.MidiEventMultiScheduler;
import com.android.internal.midi.MidiEventScheduler;
import com.android.internal.midi.MidiEventScheduler.MidiEvent;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.usb.descriptors.UsbACMidi10Endpoint;
+import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbEndpointDescriptor;
import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
@@ -45,6 +47,7 @@ import com.android.server.usb.descriptors.UsbMidiBlockParser;
import libcore.io.IoUtils;
+import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -56,7 +59,7 @@ import java.util.ArrayList;
*/
public final class UsbDirectMidiDevice implements Closeable {
private static final String TAG = "UsbDirectMidiDevice";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private Context mContext;
private String mName;
@@ -74,9 +77,6 @@ public final class UsbDirectMidiDevice implements Closeable {
private MidiDeviceServer mServer;
- // event schedulers for each input port of the physical device
- private MidiEventScheduler[] mEventSchedulers;
-
// Timeout for sending a packet to a device.
// If bulkTransfer times out, retry sending the packet up to 20 times.
private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 50;
@@ -86,8 +86,21 @@ public final class UsbDirectMidiDevice implements Closeable {
private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 200;
private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
+
+ // Array of endpoints by device connection.
private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
+
+ // Array of cable counts by device connection.
+ // Each number here maps to an entry in mInputUsbEndpoints or mOutputUsbEndpoints.
+ // This is needed because this info is part of UsbEndpointDescriptor but not UsbEndpoint.
+ private ArrayList<ArrayList<Integer>> mInputUsbEndpointCableCounts;
+ private ArrayList<ArrayList<Integer>> mOutputUsbEndpointCableCounts;
+
+ // Array of event schedulers by device connection.
+ // Each number here maps to an entry in mOutputUsbEndpoints.
+ private ArrayList<ArrayList<MidiEventMultiScheduler>> mMidiEventMultiSchedulers;
+
private ArrayList<Thread> mThreads;
private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser();
@@ -97,8 +110,6 @@ public final class UsbDirectMidiDevice implements Closeable {
private boolean mIsOpen;
private boolean mServerAvailable;
- private UsbMidiPacketConverter mUsbMidiPacketConverter;
-
private PowerBoostSetter mPowerBoostSetter = null;
private static final byte MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE = 0x02;
@@ -238,9 +249,9 @@ public final class UsbDirectMidiDevice implements Closeable {
interfaceDescriptor.getEndpointDescriptor(endpointIndex);
// 0 is output, 1 << 7 is input.
if (endpoint.getDirection() == 0) {
- numOutputs++;
+ numOutputs += getNumJacks(endpoint);
} else {
- numInputs++;
+ numInputs += getNumJacks(endpoint);
}
}
}
@@ -307,19 +318,21 @@ public final class UsbDirectMidiDevice implements Closeable {
Log.d(TAG, "openLocked()");
UsbManager manager = mContext.getSystemService(UsbManager.class);
- // Converting from raw MIDI to USB MIDI is not thread-safe.
- // UsbMidiPacketConverter creates a converter from raw MIDI
- // to USB MIDI for each USB output.
- mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs);
-
mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>();
mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
+ mInputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>();
+ mOutputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>();
+ mMidiEventMultiSchedulers = new ArrayList<ArrayList<MidiEventMultiScheduler>>();
mThreads = new ArrayList<Thread>();
for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>();
ArrayList<UsbEndpoint> outputEndpoints = new ArrayList<UsbEndpoint>();
+ ArrayList<Integer> inputEndpointCableCounts = new ArrayList<Integer>();
+ ArrayList<Integer> outputEndpointCableCounts = new ArrayList<Integer>();
+ ArrayList<MidiEventMultiScheduler> midiEventMultiSchedulers =
+ new ArrayList<MidiEventMultiScheduler>();
UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
endpointIndex++) {
@@ -328,8 +341,13 @@ public final class UsbDirectMidiDevice implements Closeable {
// 0 is output, 1 << 7 is input.
if (endpoint.getDirection() == 0) {
outputEndpoints.add(endpoint.toAndroid(mParser));
+ outputEndpointCableCounts.add(getNumJacks(endpoint));
+ MidiEventMultiScheduler scheduler =
+ new MidiEventMultiScheduler(getNumJacks(endpoint));
+ midiEventMultiSchedulers.add(scheduler);
} else {
inputEndpoints.add(endpoint.toAndroid(mParser));
+ inputEndpointCableCounts.add(getNumJacks(endpoint));
}
}
if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
@@ -341,40 +359,69 @@ public final class UsbDirectMidiDevice implements Closeable {
mUsbDeviceConnections.add(connection);
mInputUsbEndpoints.add(inputEndpoints);
mOutputUsbEndpoints.add(outputEndpoints);
+ mInputUsbEndpointCableCounts.add(inputEndpointCableCounts);
+ mOutputUsbEndpointCableCounts.add(outputEndpointCableCounts);
+ mMidiEventMultiSchedulers.add(midiEventMultiSchedulers);
}
}
- mEventSchedulers = new MidiEventScheduler[mNumOutputs];
-
- for (int i = 0; i < mNumOutputs; i++) {
- MidiEventScheduler scheduler = new MidiEventScheduler();
- mEventSchedulers[i] = scheduler;
- mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver());
+ // Set up event schedulers
+ int outputIndex = 0;
+ for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size();
+ connectionIndex++) {
+ for (int endpointIndex = 0;
+ endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size();
+ endpointIndex++) {
+ int cableCount =
+ mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex);
+ MidiEventMultiScheduler multiScheduler =
+ mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex);
+ for (int cableNumber = 0; cableNumber < cableCount; cableNumber++) {
+ MidiEventScheduler scheduler = multiScheduler.getEventScheduler(cableNumber);
+ mMidiInputPortReceivers[outputIndex].setReceiver(scheduler.getReceiver());
+ outputIndex++;
+ }
+ }
}
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
// Create input thread for each input port of the physical device
- int portNumber = 0;
+ int portStartNumber = 0;
for (int connectionIndex = 0; connectionIndex < mInputUsbEndpoints.size();
connectionIndex++) {
for (int endpointIndex = 0;
endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
endpointIndex++) {
+ // Each USB endpoint maps to one or more outputReceivers. USB MIDI data from an
+ // endpoint should be sent to the appropriate outputReceiver. A new thread is
+ // created and waits for incoming USB data. Once the data is received, it is added
+ // to the packet converter. The packet converter acts as an inverse multiplexer.
+ // With a for loop, data is pulled per cable and sent to the correct output
+ // receiver. The first byte of each legacy MIDI 1.0 USB message indicates which
+ // cable the data should be used and is how the reverse multiplexer directs data.
+ // For MIDI UMP endpoints, a multiplexer is not needed as we are just swapping
+ // the endianness of the packets.
final UsbDeviceConnection connectionFinal =
mUsbDeviceConnections.get(connectionIndex);
final UsbEndpoint endpointFinal =
mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
- final int portFinal = portNumber;
+ final int portStartFinal = portStartNumber;
+ final int cableCountFinal =
+ mInputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex);
- Thread newThread = new Thread("UsbDirectMidiDevice input thread " + portFinal) {
+ Thread newThread = new Thread("UsbDirectMidiDevice input thread "
+ + portStartFinal) {
@Override
public void run() {
final UsbRequest request = new UsbRequest();
+ final UsbMidiPacketConverter packetConverter = new UsbMidiPacketConverter();
+ packetConverter.createDecoders(cableCountFinal);
try {
request.initialize(connectionFinal, endpointFinal);
byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
- while (true) {
+ boolean keepGoing = true;
+ while (keepGoing) {
if (Thread.currentThread().interrupted()) {
Log.w(TAG, "input thread interrupted");
break;
@@ -404,42 +451,56 @@ public final class UsbDirectMidiDevice implements Closeable {
logByteArray("Input before conversion ", inputBuffer,
0, bytesRead);
}
+
+ // Add packets into the packet decoder.
+ if (!mIsUniversalMidiDevice) {
+ packetConverter.decodeMidiPackets(inputBuffer, bytesRead);
+ }
+
byte[] convertedArray;
- if (mIsUniversalMidiDevice) {
- // For USB, each 32 bit word of a UMP is
- // sent with the least significant byte first.
- convertedArray = swapEndiannessPerWord(inputBuffer,
- bytesRead);
- } else {
- if (mUsbMidiPacketConverter == null) {
- Log.w(TAG, "mUsbMidiPacketConverter is null");
- break;
+ for (int cableNumber = 0; cableNumber < cableCountFinal;
+ cableNumber++) {
+ if (mIsUniversalMidiDevice) {
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ convertedArray = swapEndiannessPerWord(inputBuffer,
+ bytesRead);
+ } else {
+ convertedArray =
+ packetConverter.pullDecodedMidiPackets(
+ cableNumber);
}
- convertedArray =
- mUsbMidiPacketConverter.usbMidiToRawMidi(
- inputBuffer, bytesRead);
- }
- if (DEBUG) {
- logByteArray("Input after conversion ", convertedArray,
- 0, convertedArray.length);
- }
+ if (DEBUG) {
+ logByteArray("Input " + cableNumber
+ + " after conversion ", convertedArray, 0,
+ convertedArray.length);
+ }
- if ((outputReceivers == null)
- || (outputReceivers[portFinal] == null)) {
- Log.w(TAG, "outputReceivers is null");
- break;
- }
- outputReceivers[portFinal].send(convertedArray, 0,
- convertedArray.length, timestamp);
-
- // Boost power if there seems to be a voice message.
- // For legacy devices, boost when message is more than size 1.
- // For UMP devices, boost for channel voice messages.
- if ((mPowerBoostSetter != null && convertedArray.length > 1)
- && (!mIsUniversalMidiDevice
- || isChannelVoiceMessage(convertedArray))) {
- mPowerBoostSetter.boostPower();
+ if (convertedArray.length == 0) {
+ continue;
+ }
+
+ if ((outputReceivers == null)
+ || (outputReceivers[portStartFinal + cableNumber]
+ == null)) {
+ Log.w(TAG, "outputReceivers is null");
+ keepGoing = false;
+ break;
+ }
+ outputReceivers[portStartFinal + cableNumber].send(
+ convertedArray, 0, convertedArray.length,
+ timestamp);
+
+ // Boost power if there seems to be a voice message.
+ // For legacy devices, boost if message length > 1.
+ // For UMP devices, boost for channel voice messages.
+ if ((mPowerBoostSetter != null
+ && convertedArray.length > 1)
+ && (!mIsUniversalMidiDevice
+ || isChannelVoiceMessage(convertedArray))) {
+ mPowerBoostSetter.boostPower();
+ }
}
}
}
@@ -455,64 +516,93 @@ public final class UsbDirectMidiDevice implements Closeable {
};
newThread.start();
mThreads.add(newThread);
- portNumber++;
+ portStartNumber += cableCountFinal;
}
}
// Create output thread for each output port of the physical device
- portNumber = 0;
+ portStartNumber = 0;
for (int connectionIndex = 0; connectionIndex < mOutputUsbEndpoints.size();
connectionIndex++) {
for (int endpointIndex = 0;
endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
endpointIndex++) {
+ // Each USB endpoint maps to one or more MIDI ports. Each port has an event
+ // scheduler that is used to pull incoming raw MIDI bytes from Android apps.
+ // With a MidiEventMultiScheduler, data can be pulled if any of the schedulers
+ // have new incoming data. This data is then packaged as USB MIDI packets before
+ // getting sent through USB. One thread will be created per endpoint that pulls
+ // data from all relevant event schedulers. Raw MIDI from the event schedulers
+ // will be converted to the correct USB MIDI format before getting sent through
+ // USB.
+
final UsbDeviceConnection connectionFinal =
mUsbDeviceConnections.get(connectionIndex);
final UsbEndpoint endpointFinal =
mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
- final int portFinal = portNumber;
- final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
-
- Thread newThread = new Thread("UsbDirectMidiDevice output thread " + portFinal) {
+ final int portStartFinal = portStartNumber;
+ final int cableCountFinal =
+ mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex);
+ final MidiEventMultiScheduler multiSchedulerFinal =
+ mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex);
+
+ Thread newThread = new Thread("UsbDirectMidiDevice output write thread "
+ + portStartFinal) {
@Override
public void run() {
try {
- while (true) {
- if (Thread.currentThread().interrupted()) {
- Log.w(TAG, "output thread interrupted");
+ final ByteArrayOutputStream midi2ByteStream =
+ new ByteArrayOutputStream();
+ final UsbMidiPacketConverter packetConverter =
+ new UsbMidiPacketConverter();
+ packetConverter.createEncoders(cableCountFinal);
+ boolean isInterrupted = false;
+ while (!isInterrupted) {
+ boolean wasSuccessful = multiSchedulerFinal.waitNextEvent();
+ if (!wasSuccessful) {
+ Log.d(TAG, "output thread closed");
break;
}
- MidiEvent event;
- try {
- event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
- } catch (InterruptedException e) {
- Log.w(TAG, "event scheduler interrupted");
- break;
- }
- if (event == null) {
- Log.w(TAG, "event is null");
- break;
+ long now = System.nanoTime();
+ for (int cableNumber = 0; cableNumber < cableCountFinal;
+ cableNumber++) {
+ MidiEventScheduler eventScheduler =
+ multiSchedulerFinal.getEventScheduler(cableNumber);
+ MidiEvent event =
+ (MidiEvent) eventScheduler.getNextEvent(now);
+ while (event != null) {
+ if (DEBUG) {
+ logByteArray("Output before conversion ",
+ event.data, 0, event.count);
+ }
+ if (mIsUniversalMidiDevice) {
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ byte[] convertedArray = swapEndiannessPerWord(
+ event.data, event.count);
+ midi2ByteStream.write(convertedArray, 0,
+ convertedArray.length);
+ } else {
+ packetConverter.encodeMidiPackets(event.data,
+ event.count, cableNumber);
+ }
+ eventScheduler.addEventToPool(event);
+ event = (MidiEvent) eventScheduler.getNextEvent(now);
+ }
}
- if (DEBUG) {
- logByteArray("Output before conversion ", event.data, 0,
- event.count);
+ if (Thread.currentThread().interrupted()) {
+ Log.d(TAG, "output thread interrupted");
+ break;
}
- byte[] convertedArray;
+ byte[] convertedArray = new byte[0];
if (mIsUniversalMidiDevice) {
- // For USB, each 32 bit word of a UMP is
- // sent with the least significant byte first.
- convertedArray = swapEndiannessPerWord(event.data,
- event.count);
+ convertedArray = midi2ByteStream.toByteArray();
+ midi2ByteStream.reset();
} else {
- if (mUsbMidiPacketConverter == null) {
- Log.w(TAG, "mUsbMidiPacketConverter is null");
- break;
- }
convertedArray =
- mUsbMidiPacketConverter.rawMidiToUsbMidi(
- event.data, event.count, portFinal);
+ packetConverter.pullEncodedMidiPackets();
}
if (DEBUG) {
@@ -520,7 +610,6 @@ public final class UsbDirectMidiDevice implements Closeable {
convertedArray.length);
}
- boolean isInterrupted = false;
// Split the packet into multiple if they are greater than the
// endpoint's max packet size.
for (int curPacketStart = 0;
@@ -558,11 +647,9 @@ public final class UsbDirectMidiDevice implements Closeable {
}
}
}
- if (isInterrupted == true) {
- break;
- }
- eventSchedulerFinal.addEventToPool(event);
}
+ } catch (InterruptedException e) {
+ Log.w(TAG, "output thread: ", e);
} catch (NullPointerException e) {
Log.e(TAG, "output thread: ", e);
}
@@ -571,7 +658,7 @@ public final class UsbDirectMidiDevice implements Closeable {
};
newThread.start();
mThreads.add(newThread);
- portNumber++;
+ portStartNumber += cableCountFinal;
}
}
@@ -667,11 +754,21 @@ public final class UsbDirectMidiDevice implements Closeable {
}
mThreads = null;
- for (int i = 0; i < mEventSchedulers.length; i++) {
+ for (int i = 0; i < mMidiInputPortReceivers.length; i++) {
mMidiInputPortReceivers[i].setReceiver(null);
- mEventSchedulers[i].close();
}
- mEventSchedulers = null;
+
+ for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size();
+ connectionIndex++) {
+ for (int endpointIndex = 0;
+ endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size();
+ endpointIndex++) {
+ MidiEventMultiScheduler multiScheduler =
+ mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex);
+ multiScheduler.close();
+ }
+ }
+ mMidiEventMultiSchedulers = null;
for (UsbDeviceConnection connection : mUsbDeviceConnections) {
connection.close();
@@ -679,8 +776,8 @@ public final class UsbDirectMidiDevice implements Closeable {
mUsbDeviceConnections = null;
mInputUsbEndpoints = null;
mOutputUsbEndpoints = null;
-
- mUsbMidiPacketConverter = null;
+ mInputUsbEndpointCableCounts = null;
+ mOutputUsbEndpointCableCounts = null;
mIsOpen = false;
}
@@ -788,4 +885,19 @@ public final class UsbDirectMidiDevice implements Closeable {
return messageType == MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE
|| messageType == MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE;
}
+
+ // Returns the number of jacks for MIDI 1.0 endpoints.
+ // For MIDI 2.0 endpoints, this concept does not exist and each endpoint should be treated as
+ // one port.
+ private int getNumJacks(UsbEndpointDescriptor usbEndpointDescriptor) {
+ UsbDescriptor classSpecificEndpointDescriptor =
+ usbEndpointDescriptor.getClassSpecificEndpointDescriptor();
+ if (classSpecificEndpointDescriptor != null
+ && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) {
+ UsbACMidi10Endpoint midiEndpoint =
+ (UsbACMidi10Endpoint) classSpecificEndpointDescriptor;
+ return midiEndpoint.getNumJacks();
+ }
+ return 1;
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
index 56bb23681741..65d7a41cf1ec 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -16,12 +16,17 @@
package com.android.server.usb;
+import android.util.Log;
+
import java.io.ByteArrayOutputStream;
/**
- * Converts between MIDI packets and USB MIDI 1.0 packets.
+ * Converts between raw MIDI packets and USB MIDI 1.0 packets.
+ * This is NOT thread-safe. Please handle locking outside this function for multiple threads.
+ * For data mapping to an invalid cable number, this converter will use the first cable.
*/
public class UsbMidiPacketConverter {
+ private static final String TAG = "UsbMidiPacketConverter";
// Refer to Table 4-1 in USB MIDI 1.0 spec.
private static final int[] PAYLOAD_SIZE = new int[]{
@@ -74,54 +79,133 @@ public class UsbMidiPacketConverter {
private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
- private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
private UsbMidiEncoder[] mUsbMidiEncoders;
+ private ByteArrayOutputStream mEncoderOutputStream = new ByteArrayOutputStream();
- public UsbMidiPacketConverter(int numEncoders) {
- mUsbMidiEncoders = new UsbMidiEncoder[numEncoders];
- for (int i = 0; i < numEncoders; i++) {
- mUsbMidiEncoders[i] = new UsbMidiEncoder();
- }
- }
+ private UsbMidiDecoder mUsbMidiDecoder;
/**
- * Converts a USB MIDI array into a raw MIDI array.
+ * Creates encoders.
*
- * @param usbMidiBytes the USB MIDI bytes to convert
- * @param size the size of usbMidiBytes
- * @return byte array of raw MIDI packets
+ * createEncoders() must be called before raw MIDI can be converted to USB MIDI.
+ *
+ * @param size the number of encoders to create
*/
- public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
- return mUsbMidiDecoder.decode(usbMidiBytes, size);
+ public void createEncoders(int size) {
+ mUsbMidiEncoders = new UsbMidiEncoder[size];
+ for (int i = 0; i < size; i++) {
+ mUsbMidiEncoders[i] = new UsbMidiEncoder(i);
+ }
}
/**
* Converts a raw MIDI array into a USB MIDI array.
*
+ * Call pullEncodedMidiPackets to retrieve the byte array.
+ *
* @param midiBytes the raw MIDI bytes to convert
* @param size the size of usbMidiBytes
* @param encoderId which encoder to use
+ */
+ public void encodeMidiPackets(byte[] midiBytes, int size, int encoderId) {
+ // Use the first encoder if the encoderId is invalid.
+ if (encoderId >= mUsbMidiEncoders.length) {
+ Log.w(TAG, "encoderId " + encoderId + " invalid");
+ encoderId = 0;
+ }
+ byte[] encodedPacket = mUsbMidiEncoders[encoderId].encode(midiBytes, size);
+ mEncoderOutputStream.write(encodedPacket, 0, encodedPacket.length);
+ }
+
+ /**
+ * Returns the encoded MIDI packets from encodeMidiPackets
+ *
* @return byte array of USB MIDI packets
*/
- public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size, int encoderId) {
- return mUsbMidiEncoders[encoderId].encode(midiBytes, size);
+ public byte[] pullEncodedMidiPackets() {
+ byte[] output = mEncoderOutputStream.toByteArray();
+ mEncoderOutputStream.reset();
+ return output;
+ }
+
+ /**
+ * Creates decoders.
+ *
+ * createDecoders() must be called before USB MIDI can be converted to raw MIDI.
+ *
+ * @param size the number of decoders to create
+ */
+ public void createDecoders(int size) {
+ mUsbMidiDecoder = new UsbMidiDecoder(size);
+ }
+
+ /**
+ * Converts a USB MIDI array into a multiple MIDI arrays, one per cable.
+ *
+ * Call pullDecodedMidiPackets to retrieve the byte array.
+ *
+ * @param usbMidiBytes the USB MIDI bytes to convert
+ * @param size the size of usbMidiBytes
+ */
+ public void decodeMidiPackets(byte[] usbMidiBytes, int size) {
+ mUsbMidiDecoder.decode(usbMidiBytes, size);
+ }
+
+ /**
+ * Returns the decoded MIDI packets from decodeMidiPackets
+ *
+ * @param cableNumber the cable to pull data from
+ * @return byte array of raw MIDI packets
+ */
+ public byte[] pullDecodedMidiPackets(int cableNumber) {
+ return mUsbMidiDecoder.pullBytes(cableNumber);
}
private class UsbMidiDecoder {
+ int mNumJacks;
+ ByteArrayOutputStream[] mDecodedByteArrays;
+
+ UsbMidiDecoder(int numJacks) {
+ mNumJacks = numJacks;
+ mDecodedByteArrays = new ByteArrayOutputStream[numJacks];
+ for (int i = 0; i < numJacks; i++) {
+ mDecodedByteArrays[i] = new ByteArrayOutputStream();
+ }
+ }
+
// Decodes the data from USB MIDI to raw MIDI.
// Each valid 4 byte input maps to a 1-3 byte output.
// Reference the USB MIDI 1.0 spec for more info.
- public byte[] decode(byte[] usbMidiBytes, int size) {
+ public void decode(byte[] usbMidiBytes, int size) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ if (size % 4 != 0) {
+ Log.w(TAG, "size " + size + " not multiple of 4");
+ }
for (int i = 0; i + 3 < size; i += 4) {
+ int cableNumber = (usbMidiBytes[i] >> 4) & 0x0f;
int codeIndex = usbMidiBytes[i] & 0x0f;
int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
if (numPayloadBytes < 0) {
continue;
}
- outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+ // Use the first cable if the cable number is invalid.
+ if (cableNumber >= mNumJacks) {
+ Log.w(TAG, "cableNumber " + cableNumber + " invalid");
+ cableNumber = 0;
+ }
+ mDecodedByteArrays[cableNumber].write(usbMidiBytes, i + 1, numPayloadBytes);
}
- return outputStream.toByteArray();
+ }
+
+ public byte[] pullBytes(int cableNumber) {
+ // Use the first cable if the cable number is invalid.
+ if (cableNumber >= mNumJacks) {
+ Log.w(TAG, "cableNumber " + cableNumber + " invalid");
+ cableNumber = 0;
+ }
+ byte[] output = mDecodedByteArrays[cableNumber].toByteArray();
+ mDecodedByteArrays[cableNumber].reset();
+ return output;
}
}
@@ -135,6 +219,13 @@ public class UsbMidiPacketConverter {
private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+ private byte mShiftedCableNumber;
+
+ UsbMidiEncoder(int cableNumber) {
+ // Jack Id is always the left nibble of every byte so shift this now.
+ mShiftedCableNumber = (byte) (cableNumber << 4);
+ }
+
// Encodes the data from raw MIDI to USB MIDI.
// Each valid 1-3 byte input maps to a 4 byte output.
// Reference the USB MIDI 1.0 spec for more info.
@@ -153,7 +244,8 @@ public class UsbMidiPacketConverter {
midiBytes[curLocation];
mNumStoredSystemExclusiveBytes++;
if (mNumStoredSystemExclusiveBytes == 3) {
- outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+ outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES
+ | mShiftedCableNumber);
outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
mNumStoredSystemExclusiveBytes = 0;
}
@@ -179,7 +271,7 @@ public class UsbMidiPacketConverter {
byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
if (curLocation + channelMessageSize <= size) {
- outputStream.write(codeIndexNumber);
+ outputStream.write(codeIndexNumber | mShiftedCableNumber);
outputStream.write(midiBytes, curLocation, channelMessageSize);
// Fill in the rest of the bytes with 0.
outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
@@ -197,8 +289,8 @@ public class UsbMidiPacketConverter {
curLocation++;
} else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
// 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
- outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
- + mNumStoredSystemExclusiveBytes);
+ outputStream.write((CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+ + mNumStoredSystemExclusiveBytes) | mShiftedCableNumber);
mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
midiBytes[curLocation];
mNumStoredSystemExclusiveBytes++;
@@ -218,7 +310,7 @@ public class UsbMidiPacketConverter {
} else {
int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
if (curLocation + systemMessageSize <= size) {
- outputStream.write(codeIndexNumber);
+ outputStream.write(codeIndexNumber | mShiftedCableNumber);
outputStream.write(midiBytes, curLocation, systemMessageSize);
// Fill in the rest of the bytes with 0.
outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
@@ -236,7 +328,7 @@ public class UsbMidiPacketConverter {
}
private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
- outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+ outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE | mShiftedCableNumber);
outputStream.write(byteToWrite);
outputStream.write(0);
outputStream.write(0);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 1f448acac5e8..117a3d9f374e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -118,7 +118,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
mClassSpecificEndpointDescriptor = descriptor;
}
- UsbDescriptor getClassSpecificEndpointDescriptor() {
+ public UsbDescriptor getClassSpecificEndpointDescriptor() {
return mClassSpecificEndpointDescriptor;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index ce1157e1d80c..7d5750e49907 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -30,11 +30,14 @@ import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.os.BatteryStatsInternal;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import com.android.internal.util.LatencyTracker;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -291,6 +294,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
try {
+ BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger(
+ SystemClock.elapsedRealtime(), mOriginatorIdentity.uid);
mCallbackDelegate.onRecognition(modelHandle, event, captureSession);
logVoidReturn("onRecognition", modelHandle, event);
} catch (Exception e) {
@@ -304,6 +309,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
int captureSession)
throws RemoteException {
try {
+ BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger(
+ SystemClock.elapsedRealtime(), mOriginatorIdentity.uid);
startKeyphraseEventLatencyTracking(event);
mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
logVoidReturn("onPhraseRecognition", modelHandle, event);
@@ -387,6 +394,12 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
}
+ private static class BatteryStatsHolder {
+ private static final BatteryStatsInternal INSTANCE =
+ LocalServices.getService(BatteryStatsInternal.class);
+ }
+
+
////////////////////////////////////////////////////////////////////////////////////////////////
// Actual logging logic below.
private static final int NUM_EVENTS_TO_DUMP = 64;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 43592348adb3..025e1dc78c86 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -30,7 +30,7 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -41,7 +41,6 @@ import android.media.AudioFormat;
import android.media.AudioManagerInternal;
import android.media.permission.Identity;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -89,8 +88,7 @@ final class HotwordDetectionConnection {
static final boolean DEBUG = false;
/**
- * For apps targeting Android API Build.VERSION_CODES#UPSIDE_DOWN_CAKE and above,
- * implementors of the HotwordDetectionService must not augment the phrase IDs which are
+ * Implementors of the HotwordDetectionService must not augment the phrase IDs which are
* supplied via HotwordDetectionService
* #onDetect(AlwaysOnHotwordDetector.EventPayload, long, HotwordDetectionService.Callback).
*
@@ -104,7 +102,7 @@ final class HotwordDetectionConnection {
* </p>
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
public static final long ENFORCE_HOTWORD_PHRASE_ID = 215066299L;
private static final String KEY_RESTART_PERIOD_IN_SECONDS = "restart_period_in_seconds";
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 6b2bea032a00..97538c1c833f 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -76,7 +76,10 @@ public final class CallControl {
}
/**
- * Request Telecom set the call state to active.
+ * Request Telecom set the call state to active. This method should be called when either an
+ * outgoing call is ready to go active or a held call is ready to go active again. For incoming
+ * calls that are ready to be answered, use
+ * {@link CallControl#answer(int, Executor, OutcomeReceiver)}.
*
* @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
* will be called on.
@@ -106,6 +109,43 @@ public final class CallControl {
}
/**
+ * Request Telecom answer an incoming call. For outgoing calls and calls that have been placed
+ * on hold, use {@link CallControl#setActive(Executor, OutcomeReceiver)}.
+ *
+ * @param videoState to report to Telecom. Telecom will store VideoState in the event another
+ * service/device requests it in order to continue the call on another screen.
+ * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+ * will be called on.
+ * @param callback that will be completed on the Telecom side that details success or failure
+ * of the requested operation.
+ *
+ * {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+ * switched the call state to active
+ *
+ * {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+ * the call state to active. A {@link CallException} will be passed
+ * that details why the operation failed.
+ */
+ public void answer(@android.telecom.CallAttributes.CallType int videoState,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<Void, CallException> callback) {
+ validateVideoState(videoState);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ if (mServerInterface != null) {
+ try {
+ mServerInterface.answer(videoState, mCallId,
+ new CallControlResultReceiver("answer", executor, callback));
+
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ } else {
+ throw new IllegalStateException(INTERFACE_ERROR_MSG);
+ }
+ }
+
+ /**
* Request Telecom set the call state to inactive. This the same as hold for two call endpoints
* but can be extended to setting a meeting to inactive.
*
@@ -343,4 +383,13 @@ public final class CallControl {
}
}
+ /** @hide */
+ private void validateVideoState(@android.telecom.CallAttributes.CallType int videoState) {
+ if (videoState != CallAttributes.AUDIO_CALL && videoState != CallAttributes.VIDEO_CALL) {
+ throw new IllegalArgumentException(TextUtils.formatSimple(
+ "The VideoState argument passed in, %d , is not a valid VideoState. The "
+ + "VideoState choices are limited to CallAttributes.AUDIO_CALL or"
+ + "CallAttributes.VIDEO_CALL", videoState));
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b6def1a22205..94c737d61b0a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -1227,6 +1227,12 @@ public final class PhoneAccount implements Parcelable {
if (hasCapabilities(CAPABILITY_VOICE_CALLING_AVAILABLE)) {
sb.append("Voice ");
}
+ if (hasCapabilities(CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)) {
+ sb.append("TransactOps ");
+ }
+ if (hasCapabilities(CAPABILITY_SUPPORTS_CALL_STREAMING)) {
+ sb.append("Stream ");
+ }
return sb.toString();
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
index 3e651e92e612..5e2c923e4c9c 100644
--- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -27,6 +27,7 @@ import android.os.ResultReceiver;
*/
oneway interface ICallControl {
void setActive(String callId, in ResultReceiver callback);
+ void answer(int videoState, String callId, in ResultReceiver callback);
void setInactive(String callId, in ResultReceiver callback);
void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback);
void startCallStreaming(String callId, in ResultReceiver callback);
diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java
index c352f2bc3f83..abcce5f61aee 100644
--- a/telephony/java/android/telephony/DomainSelectionService.java
+++ b/telephony/java/android/telephony/DomainSelectionService.java
@@ -537,9 +537,9 @@ public class DomainSelectionService extends Service {
}
@Override
- public void onWlanSelected() {
+ public void onWlanSelected(boolean useEmergencyPdn) {
try {
- mCallback.onWlanSelected();
+ mCallback.onWlanSelected(useEmergencyPdn);
} catch (Exception e) {
Rlog.e(TAG, "onWlanSelected e=" + e);
}
@@ -702,9 +702,10 @@ public class DomainSelectionService extends Service {
}
@Override
- public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
+ public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+ boolean useEmergencyPdn) {
try {
- mCallback.onDomainSelected(domain);
+ mCallback.onDomainSelected(domain, useEmergencyPdn);
} catch (Exception e) {
Rlog.e(TAG, "onDomainSelected e=" + e);
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index b9008c4ecd81..788d0e88170d 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1526,7 +1526,7 @@ public class PhoneNumberUtils {
* Formats the specified {@code phoneNumber} to the E.164 representation.
*
* @param phoneNumber the phone number to format.
- * @param defaultCountryIso the ISO 3166-1 two letters country code.
+ * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE.
* @return the E.164 representation, or null if the given phone number is not valid.
*/
public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
@@ -1537,7 +1537,7 @@ public class PhoneNumberUtils {
* Formats the specified {@code phoneNumber} to the RFC3966 representation.
*
* @param phoneNumber the phone number to format.
- * @param defaultCountryIso the ISO 3166-1 two letters country code.
+ * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE.
* @return the RFC3966 representation, or null if the given phone number is not valid.
*/
public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
diff --git a/telephony/java/android/telephony/TransportSelectorCallback.java b/telephony/java/android/telephony/TransportSelectorCallback.java
index d39679072d37..04752e418466 100644
--- a/telephony/java/android/telephony/TransportSelectorCallback.java
+++ b/telephony/java/android/telephony/TransportSelectorCallback.java
@@ -35,8 +35,10 @@ public interface TransportSelectorCallback {
/**
* Notify that WLAN transport has been selected.
+ *
+ * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not.
*/
- void onWlanSelected();
+ void onWlanSelected(boolean useEmergencyPdn);
/**
* Notify that WWAN transport has been selected.
diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java
index b3682caa4eb3..f9c2620cfaf8 100644
--- a/telephony/java/android/telephony/WwanSelectorCallback.java
+++ b/telephony/java/android/telephony/WwanSelectorCallback.java
@@ -46,6 +46,7 @@ public interface WwanSelectorCallback {
* this interface can be discarded.
*
* @param domain The selected domain.
+ * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not.
*/
- void onDomainSelected(@NetworkRegistrationInfo.Domain int domain);
+ void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn);
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 98ed1fa5ddeb..ecd703960d08 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -584,6 +584,7 @@ public class ImsCallSession {
* Gets the call ID of the session.
*
* @return the call ID
+ * If null is returned for getCallId, then that means that the call ID has not been set yet.
*/
public String getCallId() {
if (mClosed) {
@@ -1779,7 +1780,7 @@ public class ImsCallSession {
sb.append("[ImsCallSession objId:");
sb.append(System.identityHashCode(this));
sb.append(" callId:");
- sb.append(getCallId());
+ sb.append(mCallId != null ? mCallId : "[UNINITIALIZED]");
sb.append("]");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f8cf81ce4993..64454cc50a5c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -166,8 +166,8 @@ public class SatelliteManager {
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
- * Bundle key to get the respoonse from
- * {@link #sendSatelliteDatagram(long, int, SatelliteDatagram, Executor, OutcomeReceiver)}.
+ * Bundle key to get the respoonse from {@link
+ * #sendSatelliteDatagram(long, int, SatelliteDatagram, boolean, Executor, OutcomeReceiver)}.
* @hide
*/
public static final String KEY_SEND_SATELLITE_DATAGRAM = "send_satellite_datagram";
@@ -615,9 +615,7 @@ public class SatelliteManager {
/**
* The default state indicating that datagram transfer is idle.
- * This should be sent immediately after either
- * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SUCCESS} or
- * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_FAILED}.
+ * This should be sent if there are no message transfer activity happening.
*/
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0;
/**
@@ -625,25 +623,41 @@ public class SatelliteManager {
*/
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1;
/**
+ * An end state indicating that datagram sending completed successfully.
+ * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+ * will be sent if no more messages are pending.
+ */
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2;
+ /**
+ * An end state indicating that datagram sending completed with a failure.
+ * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+ * must be sent before reporting any additional datagram transfer state changes. All pending
+ * messages will be reported as failed, to the corresponding applications.
+ */
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3;
+ /**
* A transition state indicating that a datagram is being received.
*/
- public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 2;
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4;
/**
- * A transition state indicating that datagram transfer is being retried.
+ * An end state indicating that datagram receiving completed successfully.
+ * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+ * will be sent if no more messages are pending.
*/
- public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RETRYING = 3;
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5;
/**
- * An end state indicating that datagram transfer completed successfully.
+ * An end state indicating that datagram receive operation found that there are no
+ * messages to be retrieved from the satellite.
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
- * must be sent before reporting any additional datagram transfer state changes.
+ * will be sent if no more messages are pending.
*/
- public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SUCCESS = 4;
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6;
/**
- * An end state indicating that datagram transfer completed with a failure.
+ * An end state indicating that datagram receive completed with a failure.
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
- * must be sent before reporting any additional datagram transfer state changes.
+ * will be sent if no more messages are pending.
*/
- public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_FAILED = 5;
+ public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7;
/**
* The datagram transfer state is unknown. This generic datagram transfer state should be used
* only when the datagram transfer state cannot be mapped to other specific datagram transfer
@@ -655,10 +669,12 @@ public class SatelliteManager {
@IntDef(prefix = {"SATELLITE_DATAGRAM_TRANSFER_STATE_"}, value = {
SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING,
- SATELLITE_DATAGRAM_TRANSFER_STATE_RETRYING,
- SATELLITE_DATAGRAM_TRANSFER_STATE_SUCCESS,
- SATELLITE_DATAGRAM_TRANSFER_STATE_FAILED,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
@@ -685,6 +701,10 @@ public class SatelliteManager {
*/
public static final int SATELLITE_MODEM_STATE_OFF = 4;
/**
+ * Satellite modem is unavailable.
+ */
+ public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5;
+ /**
* Satellite modem state is unknown. This generic modem state should be used only when the
* modem state cannot be mapped to other specific modem states.
*/
@@ -697,6 +717,7 @@ public class SatelliteManager {
SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING,
SATELLITE_MODEM_STATE_DATAGRAM_RETRYING,
SATELLITE_MODEM_STATE_OFF,
+ SATELLITE_MODEM_STATE_UNAVAILABLE,
SATELLITE_MODEM_STATE_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
@@ -733,9 +754,6 @@ public class SatelliteManager {
* All other results indicate that this operation failed.
* Once satellite position updates begin, datagram transfer state updates will be sent
* through {@link SatellitePositionUpdateCallback}.
- * Modem should report any changes in datagram transfer state and indicate success or failure
- * by reporting {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SUCCESS} or
- * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_FAILED}.
*
* @param executor The executor on which the callback and error code listener will be called.
* @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 5dc1a6529151..d93ee217c2fb 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -31,7 +31,6 @@ oneway interface ISatellite {
* Register the callback interface with satellite service.
*
* @param listener The callback interface to handle satellite service indications.
- * @param errorCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
* SatelliteError:ERROR_NONE
@@ -43,7 +42,7 @@ oneway interface ISatellite {
* SatelliteError:REQUEST_NOT_SUPPORTED
* SatelliteError:NO_RESOURCES
*/
- void setSatelliteListener(in ISatelliteListener listener, in IIntegerConsumer errorCallback);
+ void setSatelliteListener(in ISatelliteListener listener);
/**
* Request to enable or disable the satellite service listening mode.
@@ -51,6 +50,8 @@ oneway interface ISatellite {
*
* @param enable True to enable satellite listening mode and false to disable.
* @param isDemoMode Whether demo mode is enabled.
+ * @param timeout How long the satellite modem should wait for the next incoming page before
+ * disabling listening mode.
* @param errorCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -63,7 +64,7 @@ oneway interface ISatellite {
* SatelliteError:REQUEST_NOT_SUPPORTED
* SatelliteError:NO_RESOURCES
*/
- void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode,
+ void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode, in int timeout,
in IIntegerConsumer errorCallback);
/**
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index e24e892e5b55..d9668687e6e6 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -36,10 +36,10 @@ oneway interface ISatelliteListener {
/**
* Indicates that new datagrams have been received on the device.
*
- * @param datagrams Array of new datagrams received.
- * @param pendingCount The number of datagrams that are pending.
+ * @param datagram New datagram that was received.
+ * @param pendingCount Number of additional datagrams yet to be received.
*/
- void onSatelliteDatagramsReceived(in SatelliteDatagram[] datagrams, in int pendingCount);
+ void onSatelliteDatagramReceived(in SatelliteDatagram datagram, in int pendingCount);
/**
* Indicates that the satellite has pending datagrams for the device to be pulled.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index df5143243812..711dcbe3f62b 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -63,19 +63,19 @@ public class SatelliteImplBase extends SatelliteService {
private final IBinder mBinder = new ISatellite.Stub() {
@Override
- public void setSatelliteListener(ISatelliteListener listener,
- IIntegerConsumer errorCallback) throws RemoteException {
+ public void setSatelliteListener(ISatelliteListener listener) throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this.setSatelliteListener(listener, errorCallback),
+ () -> SatelliteImplBase.this.setSatelliteListener(listener),
"setSatelliteListener");
}
@Override
public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode,
- IIntegerConsumer errorCallback) throws RemoteException {
+ int timeout, IIntegerConsumer errorCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestSatelliteListeningEnabled(enable, isDemoMode, errorCallback),
+ .requestSatelliteListeningEnabled(
+ enable, isDemoMode, timeout, errorCallback),
"requestSatelliteListeningEnabled");
}
@@ -229,7 +229,6 @@ public class SatelliteImplBase extends SatelliteService {
* Register the callback interface with satellite service.
*
* @param listener The callback interface to handle satellite service indications.
- * @param errorCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
* SatelliteError:ERROR_NONE
@@ -241,8 +240,7 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteError:REQUEST_NOT_SUPPORTED
* SatelliteError:NO_RESOURCES
*/
- public void setSatelliteListener(@NonNull ISatelliteListener listener,
- @NonNull IIntegerConsumer errorCallback) {
+ public void setSatelliteListener(@NonNull ISatelliteListener listener) {
// stub implementation
}
@@ -252,6 +250,8 @@ public class SatelliteImplBase extends SatelliteService {
*
* @param enable True to enable satellite listening mode and false to disable.
* @param isDemoMode Whether demo mode is enabled.
+ * @param timeout How long the satellite modem should wait for the next incoming page before
+ * disabling listening mode.
* @param errorCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -264,7 +264,7 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteError:REQUEST_NOT_SUPPORTED
* SatelliteError:NO_RESOURCES
*/
- public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode,
+ public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, int timeout,
@NonNull IIntegerConsumer errorCallback) {
// stub implementation
}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
index 5ee7f9abdcf1..e4f94134caa1 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
@@ -42,6 +42,10 @@ enum SatelliteModemState {
*/
SATELLITE_MODEM_STATE_OFF = 4,
/**
+ * Satellite modem is unavailable.
+ */
+ SATELLITE_MODEM_STATE_UNAVAILABLE = 5,
+ /**
* Satellite modem state is unknown. This generic modem state should be used only when the
* modem state cannot be mapped to other specific modem states.
*/
diff --git a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl
index aca83f4edbd7..6777256d171e 100644
--- a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl
@@ -22,7 +22,7 @@ import com.android.internal.telephony.IWwanSelectorCallback;
interface ITransportSelectorCallback {
oneway void onCreated(in IDomainSelector selector);
- oneway void onWlanSelected();
+ oneway void onWlanSelected(boolean useEmergencyPdn);
IWwanSelectorCallback onWwanSelected();
oneway void onWwanSelectedAsync(in ITransportSelectorResultCallback cb);
oneway void onSelectionTerminated(int cause);
diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl
index 339fbee91812..94e7c871066e 100644
--- a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl
@@ -21,6 +21,6 @@ import com.android.internal.telephony.IWwanSelectorResultCallback;
oneway interface IWwanSelectorCallback {
void onRequestEmergencyNetworkScan(in int[] preferredNetworks,
int scanType, in IWwanSelectorResultCallback cb);
- void onDomainSelected(int domain);
+ void onDomainSelected(int domain, boolean useEmergencyPdn);
void onCancel();
}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index c5169e502344..d1a68d4e9cb2 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -235,7 +235,6 @@ package android.test.mock {
method @Deprecated public android.content.Intent getLaunchIntentForPackage(String);
method @Deprecated public android.content.Intent getLeanbackLaunchIntentForPackage(String);
method @Deprecated public String getNameForUid(int);
- method @Deprecated public android.content.pm.PackageInfo getPackageArchiveInfo(String, int);
method @Deprecated public int[] getPackageGids(String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 855d3c1f4ea7..fef521163399 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -42,6 +42,8 @@ android_test {
"androidx.test.ext.junit",
"flickertestapplib",
"flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"launcher-helper-lib",
"launcher-aosp-tapl",
@@ -65,6 +67,7 @@ java_library {
],
static_libs: [
"flickerlib",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
],
@@ -80,8 +83,10 @@ java_library {
"**/helpers/*",
],
static_libs: [
- "flickerlib",
"flickertestapplib",
+ "flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
"wm-flicker-window-extensions",
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 707b522c7024..f2ffc19f2a4e 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -24,7 +24,11 @@
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="run-command" value="settings put system show_touches 1" />
+ <option name="run-command" value="settings put system pointer_location 1" />
<option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
+ <option name="teardown-command" value="settings delete system show_touches" />
+ <option name="teardown-command" value="settings delete system pointer_location" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 87bfdebec8e5..4e3ae0c0752a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -18,11 +18,13 @@ package com.android.server.wm.flicker
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.junit.FlickerBuilderProvider
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Assume
import org.junit.AssumptionViolatedException
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index e3dc6999ceb7..9dc4bf034e66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -18,12 +18,13 @@
package com.android.server.wm.flicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.component.matchers.IComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
-import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
+import android.tools.common.PlatformConsts
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.datatypes.component.IComponentNameMatcher
+import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.wm.WindowManagerTrace
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.helpers.WindowUtils
/**
* Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all
@@ -208,7 +209,7 @@ fun FlickerTest.statusBarLayerPositionAtStart(
wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
) {
// collect navbar position for the equivalent WM state
- val state = wmTrace?.firstOrNull() ?: error("WM state missing in $this")
+ val state = wmTrace?.entries?.firstOrNull() ?: error("WM state missing in $this")
val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
assertLayersStart {
@@ -224,7 +225,7 @@ fun FlickerTest.statusBarLayerPositionAtEnd(
wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
) {
// collect navbar position for the equivalent WM state
- val state = wmTrace?.lastOrNull() ?: error("WM state missing in $this")
+ val state = wmTrace?.entries?.lastOrNull() ?: error("WM state missing in $this")
val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
assertLayersEnd {
@@ -257,7 +258,7 @@ fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: ICompon
if (snapshotLayers.isNotEmpty()) {
val visibleAreas =
snapshotLayers
- .mapNotNull { snapshotLayer -> snapshotLayer.layer?.visibleRegion }
+ .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
.toTypedArray()
val snapshotRegion = RegionSubject(visibleAreas, this, timestamp)
val appVisibleRegion = it.visibleRegion(component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
index b7bdeeb7d834..7ef4d939fee2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.activityembedding
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.Before
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 7f2e057febf1..ed17059e79e7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -17,12 +17,12 @@
package com.android.server.wm.flicker.activityembedding
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 20259a7d4485..c3cbb84fd37f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -17,13 +17,13 @@
package com.android.server.wm.flicker.activityembedding
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -59,14 +59,12 @@ class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) :
@Presubmit
@Test
fun mainActivityWindowIsAlwaysVisible() {
- flicker.assertWm {
- isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- }
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
}
/**
- * Main activity surface is animated from fullscreen to ActivityEmbedding split.
- * During the transition, there is a period of time that it is covered by a snapshot of itself.
+ * Main activity surface is animated from fullscreen to ActivityEmbedding split. During the
+ * transition, there is a period of time that it is covered by a snapshot of itself.
*/
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index dba588a55c20..5dc2dd7d93a8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,12 +17,12 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index 86f52d5d362e..9fa840190fbf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.server.wm.flicker.close
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 4d2b86a00524..b042a14b30da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -17,12 +17,12 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index 470764214665..136995a78fd7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.server.wm.flicker.close
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 4d72729b368d..c4628aaa90af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -17,14 +17,14 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.LAUNCHER
import org.junit.Test
/** Base test class for transitions that close an app back to the launcher screen */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index 65d0fa991ae7..e531bc06fb3d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -17,6 +17,12 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.wm.WindowManagerState.Companion.STATE_RESUMED
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import android.util.Log
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -24,10 +30,6 @@ import androidx.window.extensions.WindowExtensions
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.embedding.ActivityEmbeddingComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.windowmanager.WindowManagerState.Companion.STATE_RESUMED
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume.assumeNotNull
class ActivityEmbeddingAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
index 73018a05bb58..afb9fbf47350 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
@@ -17,7 +17,8 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
class AppPairsHelper(
instrumentation: Instrumentation,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
index 18563ffbc0b7..7aea05d0ce9b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
@@ -19,6 +19,7 @@ package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.provider.Settings
+import android.tools.device.helpers.FIND_TIMEOUT
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
@@ -31,10 +32,10 @@ constructor(
val instr: Instrumentation,
val component: ComponentName = ActivityOptions.ASSISTANT_SERVICE_COMPONENT_NAME,
) {
- protected val uiDevice: UiDevice = UiDevice.getInstance(instr)
- protected val defaultAssistant: String? =
+ private val uiDevice: UiDevice = UiDevice.getInstance(instr)
+ private val defaultAssistant: String? =
Settings.Secure.getString(instr.targetContext.contentResolver, Settings.Secure.ASSISTANT)
- protected val defaultVoiceInteractionService: String? =
+ private val defaultVoiceInteractionService: String? =
Settings.Secure.getString(
instr.targetContext.contentResolver,
Settings.Secure.VOICE_INTERACTION_SERVICE
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index cdf7ae5fbce3..47dd4e9fb32d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class FixedOrientationAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 2ae8e1d4932a..9227e07f5b11 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -18,16 +18,16 @@
package com.android.server.wm.flicker.helpers
-import com.android.server.wm.flicker.IFlickerTestData
-import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.Rotation
+import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
/**
* Changes the device [rotation] and wait for the rotation animation to complete
*
* @param rotation New device rotation
*/
-fun IFlickerTestData.setRotation(rotation: PlatformConsts.Rotation) =
+fun IFlickerTestData.setRotation(rotation: Rotation) =
ChangeDisplayOrientationRule.setRotation(
rotation,
instrumentation,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index f5aed411ed87..79c048a14e84 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -17,13 +17,14 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class GameAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index a433b152ce73..73effbde4515 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,12 +17,14 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class ImeAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index fb0242e8e679..a6e57d5641d6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -17,12 +17,13 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class ImeEditorPopupDialogAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
index fb04b32a7bf7..d61a500e3293 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -17,26 +17,28 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.common.traces.Condition
+import android.tools.common.traces.DeviceStateDump
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.IME_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import android.view.WindowInsets.Type.ime
import android.view.WindowInsets.Type.navigationBars
import android.view.WindowInsets.Type.statusBars
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.Condition
-import com.android.server.wm.traces.common.DeviceStateDump
-import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import java.util.regex.Pattern
class ImeShownOnAppStartHelper
@JvmOverloads
constructor(
instr: Instrumentation,
- private val rotation: PlatformConsts.Rotation,
+ private val rotation: Rotation,
private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.Ime.AutoFocusActivity.LABEL,
component: ComponentNameMatcher =
@@ -55,7 +57,12 @@ constructor(
waitConditions: Array<Condition<DeviceStateDump>>
) {
super.launchViaIntent(
- wmHelper, launchedAppComponentMatcherOverride, action, stringExtras, waitConditions)
+ wmHelper,
+ launchedAppComponentMatcherOverride,
+ action,
+ stringExtras,
+ waitConditions
+ )
waitIMEShown(wmHelper)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index 8a25e36394d2..fb5e1d2da26b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class ImeStateInitializeHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
index ba8dabd4dfec..b95d86b72f34 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
@@ -17,8 +17,9 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.parser.toFlickerComponent
class LaunchBubbleHelper(instrumentation: Instrumentation) :
StandardAppHelper(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
index d6ed24a6e4af..ab916858858a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -17,13 +17,15 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Direction
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class MailAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
index ae4223251683..e93e9c8e9bfb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
@@ -19,9 +19,10 @@ package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.content.Context
import android.provider.Settings
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
import android.util.Log
import com.android.compatibility.common.util.SystemUtil
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import java.io.IOException
class MultiWindowUtils(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index 5c1eca32bbec..c547ad06fe6c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -17,13 +17,15 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class NewTasksAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index 58da2d84a5ba..20ee3b9d236d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index d7f083031ec6..78f8bcf70b60 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -17,12 +17,14 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class NotificationAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 0c8589d6515f..0e852b6c84ff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -19,17 +19,19 @@ package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.media.session.MediaController
import android.media.session.MediaSessionManager
+import android.tools.common.datatypes.Rect
+import android.tools.common.datatypes.Region
+import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.common.traces.ConditionsFactory
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import android.util.Log
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.helpers.GestureHelper.Tuple
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
-import com.android.server.wm.traces.common.region.Region
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class PipAppHelper(instrumentation: Instrumentation) :
StandardAppHelper(
@@ -93,10 +95,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
// if the distance per step is less than 1, carry out the animation in two steps
gestureHelper.pinch(
- Tuple(initLeftX, yCoord),
- Tuple(initRightX, yCoord),
- Tuple(finalLeftX, yCoord),
- Tuple(finalRightX, yCoord),
+ GestureHelper.Tuple(initLeftX, yCoord),
+ GestureHelper.Tuple(initRightX, yCoord),
+ GestureHelper.Tuple(finalLeftX, yCoord),
+ GestureHelper.Tuple(finalRightX, yCoord),
adjustedSteps
)
@@ -141,10 +143,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
// if the distance per step is less than 1, carry out the animation in two steps
gestureHelper.pinch(
- Tuple(initLeftX, yCoord),
- Tuple(initRightX, yCoord),
- Tuple(finalLeftX, yCoord),
- Tuple(finalRightX, yCoord),
+ GestureHelper.Tuple(initLeftX, yCoord),
+ GestureHelper.Tuple(initRightX, yCoord),
+ GestureHelper.Tuple(finalLeftX, yCoord),
+ GestureHelper.Tuple(finalRightX, yCoord),
adjustedSteps
)
@@ -167,7 +169,7 @@ open class PipAppHelper(instrumentation: Instrumentation) :
launchedAppComponentMatcherOverride,
action,
stringExtras,
- waitConditions = arrayOf(WindowManagerConditionsFactory.hasPipWindow())
+ waitConditions = arrayOf(ConditionsFactory.hasPipWindow())
)
wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 8f54000a96d3..06e668ea07a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index 61dabfc422a1..94c90dabd2c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class ShowWhenLockedAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index 9ed9d28efd36..64af811d1c4f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 8f7049acbe07..316766a5c3f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,13 +17,15 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper
@JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 092a4fd8a10a..c23cf34be60a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -17,16 +17,16 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -86,9 +86,7 @@ class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker)
if (imeSnapshotLayers.isNotEmpty()) {
val visibleAreas =
imeSnapshotLayers
- .mapNotNull { imeSnapshotLayer ->
- imeSnapshotLayer.layer?.visibleRegion
- }
+ .mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion }
.toTypedArray()
val imeVisibleRegion = RegionSubject(visibleAreas, this, timestamp)
val appVisibleRegion = it.visibleRegion(imeTestApp)
@@ -105,7 +103,7 @@ class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker)
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 0870cec9ecab..823328af57aa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -18,15 +18,15 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -117,7 +117,7 @@ open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) {
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
index 96b23bc01a79..0fe52df5bb25 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index 48dbf25cb5b5..a4e4b6f40867 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -17,15 +17,15 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -96,7 +96,7 @@ open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest(
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// b/190352379 (IME doesn't show on app launch in 90 degrees)
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
index ed5d3096d039..5aacb3011e79 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index 7b935ff344a7..e85da1f09772 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -17,15 +17,15 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -90,7 +90,7 @@ open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : B
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
// b/190352379 (IME doesn't show on app launch in 90 degrees)
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
index 0a899914a35d..eb81aed35011 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 1a0c95958a29..1b4d6ad4422f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -19,15 +19,15 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
index 37e8c6baf677..db1440b0c5b8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 0b7b165aaad6..e2d6dbf428f9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -19,16 +19,16 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -93,7 +93,7 @@ open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(f
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
index 116bc1bf6f20..405ab6bcd9d6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index dcffa4785f6d..579c10f62f52 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,8 +18,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
fun FlickerTest.imeLayerBecomesVisible() {
assertLayers {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index defb43755841..3a8db45ab767 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -16,17 +16,18 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Postsubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -107,10 +108,9 @@ class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flick
return FlickerTestFactory.nonRotationTests(
supportedRotations =
listOf(
- PlatformConsts.Rotation.ROTATION_90,
+ Rotation.ROTATION_90,
),
- supportedNavigationModes =
- listOf(PlatformConsts.NavBar.MODE_3BUTTON, PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_3BUTTON, NavBar.MODE_GESTURAL)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 89d37dbbb780..1fee20d7803a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -18,17 +18,17 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -64,7 +64,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl
}
transitions {
// Bring the existing IME activity to the front in landscape mode device rotation.
- setRotation(PlatformConsts.Rotation.ROTATION_90)
+ setRotation(Rotation.ROTATION_90)
imeTestApp.launchViaIntent(wmHelper)
}
teardown { imeTestApp.exit(wmHelper) }
@@ -99,7 +99,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_90)
+ supportedRotations = listOf(Rotation.ROTATION_90)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
index 307821f2d151..3aca2a07f7cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index c72e4e47c951..6179fc2aef21 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -17,17 +17,17 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.reopenAppFromOverview
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -142,7 +142,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTes
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 167689cd1c18..954f589ffa7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -18,18 +18,19 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -133,8 +134,8 @@ open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: Flicker
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL),
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
index 82c38a31f128..0f57467c0449 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt
index 6c6003f8e64f..a927102f2a08 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index e7cfb9e7ab78..7514c9befe4f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -17,17 +17,17 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -123,7 +123,7 @@ open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTes
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt
index e7ecb87ada24..194c86be3207 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 851651eea91c..d1335294f629 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -18,14 +18,14 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,7 +81,7 @@ open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest(
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
index 0c5315524314..f5b22949e6b4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 605821225c1f..99b9bd2bfc66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -17,18 +17,18 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import android.view.WindowInsets.Type.ime
import android.view.WindowInsets.Type.navigationBars
import android.view.WindowInsets.Type.statusBars
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
@@ -91,7 +91,7 @@ class ShowImeWhileDismissingThemedPopupDialogTest(flicker: FlickerTest) : BaseTe
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 5d963467229f..9ea12a9e22a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -17,19 +17,19 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.ConditionsFactory
+import android.tools.device.flicker.isShellTransitionsEnabled
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -82,7 +82,7 @@ open class ShowImeWhileEnteringOverviewTest(flicker: FlickerTest) : BaseTest(fli
private fun waitNavStatusBarVisibility(stateSync: WindowManagerStateHelper.StateSyncBuilder) {
when {
flicker.scenario.isLandscapeOrSeascapeAtStart && !flicker.scenario.isTablet ->
- stateSync.add(WindowManagerConditionsFactory.isStatusBarVisible().negate())
+ stateSync.add(ConditionsFactory.isStatusBarVisible().negate())
else -> stateSync.withNavOrTaskBarVisible().withStatusBarVisible()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
index 9308fbbc3f06..fc3971351db7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 7979cf9b7ba5..e8f9aa3038ef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -17,16 +17,16 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
index 549b929a6e81..8b89a8b4c40d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.launch
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
index 09422876bd9b..549183f407e2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -16,12 +16,12 @@
package com.android.server.wm.flicker.launch
+import android.tools.device.apphelpers.CameraAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.helpers.CameraAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 9d86f8c8dc81..19ecf6ab8799 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -16,13 +16,14 @@
package com.android.server.wm.flicker.launch
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -61,7 +62,7 @@ class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(
if (flicker.scenario.isTablet) {
tapl.setExpectedRotation(flicker.scenario.startRotation.value)
} else {
- tapl.setExpectedRotation(PlatformConsts.Rotation.ROTATION_0.value)
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
}
RemoveAllTasksButHomeRule.removeAllTasksButHome()
}
@@ -87,7 +88,7 @@ class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(
fun getParams(): Collection<FlickerTest> {
// TAPL fails on landscape mode b/240916028
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_3BUTTON)
+ supportedNavigationModes = listOf(NavBar.MODE_3BUTTON)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 9fbec973e93f..8fdbb6445bac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -17,14 +17,14 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
index 2f0e56f1a276..1a1d4036579f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -17,9 +17,9 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTest
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Test
/** Base class for app launch tests */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index fb6fb22b0b0f..63ffee6fd77b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -21,12 +21,12 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.ClassRule
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index 32276d6f151b..a221ef6963c3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -20,13 +20,13 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.ClassRule
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index ff39611fbbf2..4efee55b97b5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -19,14 +19,14 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,8 +46,7 @@ import org.junit.runners.Parameterized
@Postsubmit
class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) :
OpenAppFromLockNotificationCold(flicker) {
- private val showWhenLockedApp: ShowWhenLockedAppHelper =
- ShowWhenLockedAppHelper(instrumentation)
+ private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
// Although we are technically still locked here, the overlay app means we should open the
// notification shade as if we were unlocked.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
index aa054a9333f1..730f78ff3bc2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -18,11 +18,11 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Assume
import org.junit.Ignore
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
index 0ed3bba8311e..9c16b7938e73 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -18,13 +18,13 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index af6c81d454b7..4a9507aabf75 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -18,24 +18,24 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.navBarWindowIsVisibleAtEnd
import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 2b16ef0de5a8..00d7544f7217 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -18,14 +18,14 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -74,7 +74,7 @@ open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTr
if (flicker.scenario.isTablet) {
tapl.setExpectedRotation(flicker.scenario.startRotation.value)
} else {
- tapl.setExpectedRotation(PlatformConsts.Rotation.ROTATION_0.value)
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
}
tapl.workspace.switchToOverview()
wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index 1c979e825793..ff24190a7aef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -16,10 +16,10 @@
package com.android.server.wm.flicker.launch
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 55e7a9926a04..9ab61566e13f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -19,14 +19,15 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -213,8 +214,8 @@ open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTrans
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL),
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 618fb8ab76ea..e0db96f3b5c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -17,14 +17,14 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Test
/** Base class for app launch tests */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 93bf09995984..cdd2d45769bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -18,13 +18,13 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 8d2af38c8898..6005a81aac9e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -17,16 +17,16 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
+import android.tools.device.apphelpers.CameraAppHelper
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import android.view.KeyEvent
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
-import com.android.server.wm.flicker.helpers.CameraAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index c78d0e9c5c5f..da985232a1e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -20,21 +20,20 @@ import android.app.Instrumentation
import android.os.Bundle
import android.os.Handler
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.ConditionsFactory
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.R
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.junit.FlickerBuilderProvider
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,7 +57,7 @@ import org.junit.runners.Parameterized
class OverrideTaskTransitionTest(val flicker: FlickerTest) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+ private val testApp = SimpleAppHelper(instrumentation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -75,7 +74,7 @@ class OverrideTaskTransitionTest(val flicker: FlickerTest) {
)
wmHelper
.StateSyncBuilder()
- .add(WindowManagerConditionsFactory.isWMStateComplete())
+ .add(ConditionsFactory.isWMStateComplete())
.withAppTransitionIdle()
.withWindowSurfaceAppeared(testApp)
.waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 94afd815921c..dd9e4cffcd30 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,23 +21,23 @@ import android.app.WallpaperManager
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.DEFAULT_TASK_DISPLAY_AREA
+import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
+import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
+import android.tools.common.datatypes.component.ComponentSplashScreenMatcher
+import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.NewTasksAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.DEFAULT_TASK_DISPLAY_AREA
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.SPLASH_SCREEN
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
-import com.android.server.wm.traces.common.component.matchers.ComponentSplashScreenMatcher
-import com.android.server.wm.traces.common.component.matchers.IComponentMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
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 b7faf8325fc2..78cee3c4e71c 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
@@ -17,18 +17,18 @@
package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.datatypes.Rect
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -237,7 +237,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(fl
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
index e6cdd1efa798..f970a79abcb8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
@@ -16,11 +16,10 @@
package com.android.server.wm.flicker.quickswitch
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -32,13 +31,11 @@ import org.junit.runners.Parameterized
class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: FlickerTest) :
QuickSwitchBetweenTwoAppsBackTest(flicker) {
companion object {
- private var startDisplayBounds = Rect.EMPTY
-
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 25d9753b363f..2b69e9b7d258 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
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 6294761ba1bf..cd7d6fac0e9c 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
@@ -17,18 +17,18 @@
package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.datatypes.Rect
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -255,7 +255,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
index aa9adf0116ae..9f48cdae20f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
@@ -16,11 +16,10 @@
package com.android.server.wm.flicker.quickswitch
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -32,13 +31,11 @@ import org.junit.runners.Parameterized
class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: FlickerTest) :
QuickSwitchBetweenTwoAppsForwardTest(flicker) {
companion object {
- private var startDisplayBounds = Rect.EMPTY
-
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index b40ecac2d19a..b0d4e2753758 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index c03cd2954e25..63299cb6cd7a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -18,17 +18,18 @@ package com.android.server.wm.flicker.quickswitch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.isShellTransitionsEnabled
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
-import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -283,9 +284,9 @@ open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker)
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL),
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
// TODO: Test with 90 rotation
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
index 8b216035f9f8..af671df194ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
@@ -16,10 +16,11 @@
package com.android.server.wm.flicker.quickswitch
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.service.PlatformConsts
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -35,9 +36,9 @@ open class QuickSwitchFromLauncherTestCfArm(flicker: FlickerTest) :
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL),
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
// TODO: Test with 90 rotation
- supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ supportedRotations = listOf(Rotation.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index e7e39c69f5fb..4a4180b6bbff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -18,13 +18,13 @@ package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
index 6420f79a01f1..0e6b20fc21c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.rotation
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 74ecddeb359a..3c0bbd6c4c1c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,12 +17,12 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.BaseTest
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.Test
/** Base class for app rotation tests */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 1a69344ded40..17b3b2b97e4b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -18,16 +18,16 @@ package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.ScenarioBuilder
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerBuilder
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.ScenarioBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -108,8 +108,10 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl
fun appWindowFullScreen() {
flicker.assertWm {
this.invoke("isFullScreen") {
- val appWindow = it.windowState(testApp.`package`)
- val flags = appWindow.windowState?.attributes?.flags ?: 0
+ val appWindow =
+ it.windowState(testApp.`package`)
+ ?: error("App window for package ${testApp.`package`} not found")
+ val flags = appWindow.windowState.attributes.flags
appWindow
.check { "isFullScreen" }
.that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
@@ -124,8 +126,10 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl
fun appWindowSeamlessRotation() {
flicker.assertWm {
this.invoke("isRotationSeamless") {
- val appWindow = it.windowState(testApp.`package`)
- val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+ val appWindow =
+ it.windowState(testApp.`package`)
+ ?: error("App window for package ${testApp.`package`} not found")
+ val rotationAnimation = appWindow.windowState.attributes.rotationAnimation
appWindow
.check { "isRotationSeamless" }
.that(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
index 0ebbf4eb6eae..b236d87616ab 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.rotation
-import com.android.server.wm.flicker.FlickerTest
-import com.android.server.wm.flicker.FlickerTestFactory
-import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index 9b1262dd193b..75e35ee9c765 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -41,6 +41,7 @@ android_test {
"kotlin-stdlib",
"kotlinx-coroutines-android",
"wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
"wm-flicker-window-extensions",
],
}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index c7e5a5ea3311..e59071bd1d88 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Debug.MemoryInfo;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
@@ -40,6 +41,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This test is intended to measure the amount of memory applications use when
@@ -313,17 +315,19 @@ public class MemoryUsageTest extends InstrumentationTestCase {
public void run() {
try {
- String mimeType = mLaunchIntent.getType();
- if (mimeType == null && mLaunchIntent.getData() != null
+ AtomicReference<String> mimeType = new AtomicReference<>(mLaunchIntent.getType());
+ if (mimeType.get() == null && mLaunchIntent.getData() != null
&& "content".equals(mLaunchIntent.getData().getScheme())) {
- mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
- UserHandle.USER_CURRENT);
+ mAm.getMimeTypeFilterAsync(mLaunchIntent.getData(), UserHandle.USER_CURRENT,
+ new RemoteCallback(result -> {
+ mimeType.set(result.getPairValue());
+ }));
}
mAtm.startActivityAndWait(null,
getInstrumentation().getContext().getBasePackageName(),
getInstrumentation().getContext().getAttributionTag(), mLaunchIntent,
- mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
+ mimeType.get(), null, null, 0, mLaunchIntent.getFlags(), null, null,
UserHandle.USER_CURRENT_OR_SELF);
} catch (RemoteException e) {
Log.w(TAG, "Error launching app", e);
diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp
new file mode 100644
index 000000000000..254770d21818
--- /dev/null
+++ b/tests/MidiTests/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // 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: "MidiTests",
+ srcs: ["**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "services.midi",
+ "truth-prebuilt",
+ ],
+ jni_libs: ["libdexmakerjvmtiagent"],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/tests/MidiTests/AndroidManifest.xml b/tests/MidiTests/AndroidManifest.xml
new file mode 100644
index 000000000000..0ee1b4493764
--- /dev/null
+++ b/tests/MidiTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.midi" >
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.midi"
+ android:label="MidiTests"/>
+</manifest>
diff --git a/tests/MidiTests/AndroidTest.xml b/tests/MidiTests/AndroidTest.xml
new file mode 100644
index 000000000000..9320f0aac090
--- /dev/null
+++ b/tests/MidiTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs sample instrumentation test.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="MidiTests.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="MidiTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.midi"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/MidiTests/OWNERS b/tests/MidiTests/OWNERS
new file mode 100644
index 000000000000..af273a6f50e0
--- /dev/null
+++ b/tests/MidiTests/OWNERS
@@ -0,0 +1 @@
+include /services/midi/OWNERS
diff --git a/tests/MidiTests/TEST_MAPPING b/tests/MidiTests/TEST_MAPPING
new file mode 100644
index 000000000000..60416a8ab3f9
--- /dev/null
+++ b/tests/MidiTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MidiTests"
+ }
+ ]
+}
diff --git a/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java
new file mode 100644
index 000000000000..1659cc07f021
--- /dev/null
+++ b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.midi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.midi.MidiEventMultiScheduler;
+import com.android.internal.midi.MidiEventScheduler;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+/**
+ * Unit tests for com.android.internal.midi.MidiEventMultiScheduler.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MidiEventMultiSchedulerTest {
+ private byte[] generateRandomByteStream(Random rnd, int size) {
+ byte[] output = new byte[size];
+ rnd.nextBytes(output);
+ return output;
+ }
+
+ private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
+ assertEquals(expectedArray.length, outputArray.length);
+ for (int i = 0; i < outputArray.length; i++) {
+ assertEquals(expectedArray[i], outputArray[i]);
+ }
+ }
+
+ private long timeFromNow(long milliseconds) {
+ return System.nanoTime() + 1000000L * milliseconds;
+ }
+
+ @Test
+ public void testMultiScheduler() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7},
+ 0, 2, timeFromNow(100)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2},
+ 0, 2, timeFromNow(200)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4},
+ 0, 2, timeFromNow(300)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
+ 0, 2, timeFromNow(400)));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNotNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNotNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerLargeData() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+
+ Random rnd = new Random(42);
+
+ final int arraySize = 1000;
+ byte[] expectedArray = generateRandomByteStream(rnd, arraySize);
+
+ scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize,
+ timeFromNow(100)));
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ compareByteArrays(expectedArray, event.data);
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ scheduler0.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerMultiClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ multiScheduler.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerNoPreemptiveClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+ scheduler0.close();
+ scheduler1.close();
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
+ 0, 2, timeFromNow(100)));
+ assertTrue(multiScheduler.waitNextEvent());
+ scheduler2.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerSpamEvents() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals(msg[0], event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testSchedulerSpamEventsPullLater() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ }
+
+ for (int i = 0; i < 1000; i++) {
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) i, event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testSchedulerSpamEventsCallbackLater() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ }
+
+ for (int i = 0; i < 1000; i++) {
+ try {
+ assertTrue(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+ }
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) i, event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testMultiSchedulerOutOfOrder() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
+ 0, 1,
+ timeFromNow(400)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
+ 0, 1,
+ timeFromNow(300)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
+ 0, 1,
+ timeFromNow(200)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
+ 0, 1,
+ timeFromNow(100)));
+
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf0, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf1, event.data[0]);
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf2, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf3, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testMultiSchedulerOutOfOrderNegativeTime() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
+ 0, 1,
+ timeFromNow(-100)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
+ 0, 1,
+ timeFromNow(-200)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
+ 0, 1,
+ timeFromNow(-300)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
+ 0, 1,
+ timeFromNow(-400)));
+
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf0, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf1, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf2, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf3, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
index 7a8d9490b540..97398dc4e334 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -15,7 +15,7 @@
*/
package com.android.test
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import org.junit.Test
@@ -170,4 +170,4 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
assertTrue(failures)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
index da53387c935c..0cc18d657cf5 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
@@ -16,10 +16,11 @@
package com.android.test
import android.graphics.Point
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
+import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -45,8 +46,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
activity.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */)
}
// Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
- LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist()
-
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
// Verify the next buffer is submitted with the correct size
LayersTraceSubject(trace).layer("SurfaceView", 3).also {
it.hasBufferSize(defaultBufferSize)
@@ -82,7 +84,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
// verify buffer size is reset to default buffer size
LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist()
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@@ -110,7 +114,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
// verify buffer size is reset to default buffer size
LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist()
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
LayersTraceSubject(trace).layer("SurfaceView", 3)
.hasBufferOrientation(Transform.ROT_90.value)
@@ -144,10 +150,11 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
for (count in 0 until 5) {
LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 1L)
.hasBufferSize(defaultBufferSize)
- LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 2L)
- .doesNotExist()
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 2L)
+ }
LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 3L)
.hasBufferSize(bufferSize)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
index 2d6c664cca02..6f4d11c3aa1b 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
@@ -19,11 +19,12 @@ import android.graphics.Color
import android.graphics.Point
import android.graphics.Rect
import android.os.SystemClock
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
+import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -103,7 +104,9 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
// verify buffer size is reset to default buffer size
LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist()
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@@ -221,4 +224,4 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
it.hasBufferSize(defaultBufferSize)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
index cf4186d84e2d..e722ba537a8e 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -16,9 +16,10 @@
package com.android.test
import android.graphics.Point
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
+import org.junit.Assert
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -70,7 +71,9 @@ class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
// verify buffer size is reset to default buffer size
LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist()
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
index 61d4095c7cf3..be3ed715d4e2 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -17,7 +17,7 @@ package com.android.test
import android.graphics.Color
import android.graphics.Rect
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -87,4 +87,4 @@ class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(u
checkPixels(svBounds, Color.BLUE)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
index 6383da5a0a98..cf4cb8c97ea1 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -21,9 +21,11 @@ import android.graphics.Color
import android.graphics.Rect
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.traces.common.layers.LayersTrace
+import android.tools.common.flicker.subject.layers.LayerSubject
+import android.tools.common.traces.surfaceflinger.LayersTrace
+import android.tools.device.traces.io.ResultWriter
+import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
+import android.tools.device.traces.monitors.withSFTracing
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -52,8 +54,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
- return withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ return withSFTracing(TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
predicate(it)
}
@@ -61,8 +62,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
fun withTrace(predicate: () -> Unit): LayersTrace {
- return withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ return withSFTracing(TRACE_FLAGS) {
predicate()
}
}
@@ -84,8 +84,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
private fun stopLayerTrace() {
- val tmpDir = instrumentation.targetContext.dataDir.toPath()
- LayersTraceMonitor(tmpDir).stop()
+ LayersTraceMonitor().stop(ResultWriter())
}
fun checkPixels(bounds: Rect, @ColorInt color: Int) {
@@ -117,4 +116,4 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
private const val TRACE_FLAGS =
(1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
index 093c3125f253..bba967815ba5 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -18,6 +18,8 @@ package com.android.test
import android.app.Instrumentation
import android.graphics.Point
import android.provider.Settings
+import android.tools.common.datatypes.Size
+import android.tools.common.flicker.subject.layers.LayerSubject
import androidx.test.InstrumentationRegistry
import org.junit.After
import org.junit.Before
@@ -69,6 +71,10 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
const val R8G8B8A8_UNORM = 1
val defaultBufferSize = Point(640, 480)
+ fun LayerSubject.hasBufferSize(point: Point) = hasBufferSize(Size.from(point.x, point.y))
+
+ fun LayerSubject.hasLayerSize(point: Point) = hasLayerSize(Size.from(point.x, point.y))
+
// system/window.h definitions
enum class ScalingMode() {
FREEZE, // = 0
@@ -94,4 +100,4 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
INVERSE_DISPLAY(0x08)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index 722e671266e1..6f4f7b13af66 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -16,14 +16,15 @@
package com.android.test.taskembed
import android.app.Instrumentation
-import android.graphics.Point
import android.graphics.Rect
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import android.tools.common.datatypes.Size
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.device.traces.io.ResultWriter
+import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
+import android.tools.device.traces.monitors.withSFTracing
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
@@ -46,8 +47,10 @@ class ResizeTasksSyncTest {
@Before
fun setup() {
- val tmpDir = instrumentation.targetContext.dataDir.toPath()
- LayersTraceMonitor(tmpDir).stop()
+ val monitor = LayersTraceMonitor()
+ if (monitor.isEnabled) {
+ monitor.stop(ResultWriter())
+ }
val firstTaskBounds = Rect(0, 0, 1080, 1000)
val secondTaskBounds = Rect(0, 1000, 1080, 2000)
@@ -68,8 +71,7 @@ class ResizeTasksSyncTest {
val firstBounds = Rect(0, 0, 1080, 800)
val secondBounds = Rect(0, 1000, 1080, 1800)
- val trace = withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ val trace = withSFTracing(TRACE_FLAGS) {
lateinit var resizeReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds)
@@ -91,13 +93,13 @@ class ResizeTasksSyncTest {
// verify buffer size should be changed to expected values.
LayersTraceSubject(trace).layer(FIRST_ACTIVITY, frame.toLong()).also {
- val firstTaskSize = Point(firstBounds.width(), firstBounds.height())
+ val firstTaskSize = Size.from(firstBounds.width(), firstBounds.height())
it.hasLayerSize(firstTaskSize)
it.hasBufferSize(firstTaskSize)
}
LayersTraceSubject(trace).layer(SECOND_ACTIVITY, frame.toLong()).also {
- val secondTaskSize = Point(secondBounds.width(), secondBounds.height())
+ val secondTaskSize = Size.from(secondBounds.width(), secondBounds.height())
it.hasLayerSize(secondTaskSize)
it.hasBufferSize(secondTaskSize)
}
@@ -108,4 +110,4 @@ class ResizeTasksSyncTest {
private const val FIRST_ACTIVITY = "Activity1"
private const val SECOND_ACTIVITY = "Activity2"
}
-} \ No newline at end of file
+}
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java
new file mode 100644
index 000000000000..ad701e5117fc
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Unit tests for com.android.server.usb.UsbMidiPacketConverter.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UsbMidiPacketConverterTest {
+ private byte[] generateRandomByteStream(Random rnd, int size) {
+ byte[] output = new byte[size];
+ rnd.nextBytes(output);
+ return output;
+ }
+
+ private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
+ assertEquals(expectedArray.length, outputArray.length);
+ for (int i = 0; i < outputArray.length; i++) {
+ assertEquals(expectedArray[i], outputArray[i]);
+ }
+ }
+
+ @Test
+ public void testDecoderSinglePacket() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(2);
+ byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66};
+ byte[] expectedOutputCable0 = new byte[] {};
+ byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ }
+
+ @Test
+ public void testDecoderMultiplePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(4);
+ byte[] input = new byte[] {
+ 0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E,
+ 0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00,
+ 0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12};
+ byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E};
+ byte[] expectedOutputCable2 = new byte[] {};
+ byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2);
+ byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ compareByteArrays(expectedOutputCable2, actualOutputCable2);
+ compareByteArrays(expectedOutputCable3, actualOutputCable3);
+ }
+
+ @Test
+ public void testDecoderSysExEndFirstByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(2);
+ byte[] input = new byte[] {
+ 0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {};
+ byte[] expectedOutputCable1 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ }
+
+ @Test
+ public void testDecoderSysExEndSecondByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] input = new byte[] {
+ 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ 0x02, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExEndThirdByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExStartEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExStartByteEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderDefaultToFirstCable() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderLargePacketDoesNotCrash() {
+ for (long seed = 1001; seed < 5000; seed += 777) {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(3);
+ Random rnd = new Random(seed);
+ byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ usbMidiPacketConverter.pullDecodedMidiPackets(2);
+ }
+ }
+
+ @Test
+ public void testEncoderBasic() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66};
+ byte[] expectedOutput = new byte[] {
+ 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderMultiplePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(3);
+ byte[] inputCable2 = new byte[] {
+ (byte) 0xB4 /* Control Change */, 0x55, 0x6E};
+ byte[] inputCable1 = new byte[] {
+ (byte) 0xF8 /* Timing Clock (Single Byte) */,
+ (byte) 0xF3 /* Song Select (Two Bytes) */, 0x12};
+ byte[] expectedOutput = new byte[] {
+ 0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E,
+ 0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00,
+ 0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderWeavePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(2);
+ byte[] inputCable1Msg1 = new byte[] {
+ (byte) 0x93 /* Note-On */, 0x23, 0x43};
+ byte[] inputCable0Msg = new byte[] {
+ (byte) 0xB4 /* Control Change */, 0x65, 0x26};
+ byte[] inputCable1Msg2 = new byte[] {
+ (byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76};
+ byte[] expectedOutput = new byte[] {
+ 0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43,
+ 0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26,
+ 0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76};
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndFirstByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndSecondByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ 0x02, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndThirdByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ 0x02, 0x03, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExStartEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExStartByteEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderMultiplePulls() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x44, 0x55,
+ 0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+
+ input = new byte[] {
+ 0x11, // Combined with 0x66 and 0x77 above
+ 0x22, (byte) 0xF7 /* SysEx End */};
+ expectedOutput = new byte[] {
+ 0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11,
+ 0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+
+ input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
+ expectedOutput = new byte[] {
+ 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderDefaultToFirstCable() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(2);
+ byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33};
+ byte[] expectedOutput = new byte[] {
+ 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderLargePacketDoesNotCrash() {
+ for (long seed = 234; seed < 4000; seed += 666) {
+ Random rnd = new Random(seed);
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(4);
+ for (int cableNumber = 0; cableNumber < 4; cableNumber++) {
+ byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber);
+ }
+ usbMidiPacketConverter.pullEncodedMidiPackets();
+ }
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final int bufferSize = 30;
+ final int numCables = 16;
+ final int bytesToEncodePerEncoding = 10;
+ byte[][] rawMidi = new byte[numCables][bufferSize];
+ for (long seed = 45; seed < 3000; seed += 300) {
+ Random rnd = new Random(seed);
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize);
+
+ // Change the last byte to SysEx End.
+ // This way the encoder is guaranteed to flush all packets.
+ rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7;
+ }
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(numCables);
+ // Encode packets and interweave them
+ for (int startByte = 0; startByte < bufferSize;
+ startByte += bytesToEncodePerEncoding) {
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte,
+ startByte + bytesToEncodePerEncoding);
+ usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length,
+ cableNumber);
+ }
+ }
+ byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets();
+
+ usbMidiPacketConverter.createDecoders(numCables);
+
+ // Now decode the MIDI packets to check if they are the same as the original
+ usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length);
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber);
+ compareByteArrays(rawMidi[cableNumber], decodedRawMidi);
+ }
+ }
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
index 161c83ce2fab..1fb1c630304d 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
@@ -24,11 +24,12 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.ArraySet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Objects;
+import java.util.Set;
/**
* A data class representing a known Wi-Fi network.
@@ -59,7 +60,7 @@ public final class KnownNetwork implements Parcelable {
@NetworkSource private final int mNetworkSource;
private final String mSsid;
- @SecurityType private final int[] mSecurityTypes;
+ @SecurityType private final ArraySet<Integer> mSecurityTypes;
private final DeviceInfo mDeviceInfo;
/**
@@ -68,11 +69,9 @@ public final class KnownNetwork implements Parcelable {
public static final class Builder {
@NetworkSource private int mNetworkSource = -1;
private String mSsid;
- @SecurityType private int[] mSecurityTypes;
+ @SecurityType private final ArraySet<Integer> mSecurityTypes = new ArraySet<>();
private android.net.wifi.sharedconnectivity.app.DeviceInfo mDeviceInfo;
- public Builder() {}
-
/**
* Sets the indicated source of the known network.
*
@@ -98,14 +97,14 @@ public final class KnownNetwork implements Parcelable {
}
/**
- * Sets the security types of the known network.
+ * Adds a security type of the known network.
*
- * @param securityTypes The array of security types supported by the known network.
+ * @param securityType A security type supported by the known network.
* @return Returns the Builder object.
*/
@NonNull
- public Builder setSecurityTypes(@NonNull @SecurityType int[] securityTypes) {
- mSecurityTypes = securityTypes;
+ public Builder addSecurityType(@SecurityType int securityType) {
+ mSecurityTypes.add(securityType);
return this;
}
@@ -136,7 +135,7 @@ public final class KnownNetwork implements Parcelable {
}
}
- private static void validate(int networkSource, String ssid, int [] securityTypes) {
+ private static void validate(int networkSource, String ssid, Set<Integer> securityTypes) {
if (networkSource != NETWORK_SOURCE_CLOUD_SELF && networkSource
!= NETWORK_SOURCE_NEARBY_SELF) {
throw new IllegalArgumentException("Illegal network source");
@@ -144,7 +143,7 @@ public final class KnownNetwork implements Parcelable {
if (TextUtils.isEmpty(ssid)) {
throw new IllegalArgumentException("SSID must be set");
}
- if (securityTypes == null || securityTypes.length == 0) {
+ if (securityTypes.isEmpty()) {
throw new IllegalArgumentException("SecurityTypes must be set");
}
}
@@ -152,12 +151,12 @@ public final class KnownNetwork implements Parcelable {
private KnownNetwork(
@NetworkSource int networkSource,
@NonNull String ssid,
- @NonNull @SecurityType int[] securityTypes,
+ @NonNull @SecurityType ArraySet<Integer> securityTypes,
@NonNull DeviceInfo deviceInfo) {
validate(networkSource, ssid, securityTypes);
mNetworkSource = networkSource;
mSsid = ssid;
- mSecurityTypes = securityTypes;
+ mSecurityTypes = new ArraySet<>(securityTypes);
mDeviceInfo = deviceInfo;
}
@@ -184,11 +183,11 @@ public final class KnownNetwork implements Parcelable {
/**
* Gets the security types of the known network.
*
- * @return Returns the array of security types supported by the known network.
+ * @return Returns a set with security types supported by the known network.
*/
@NonNull
@SecurityType
- public int[] getSecurityTypes() {
+ public Set<Integer> getSecurityTypes() {
return mSecurityTypes;
}
@@ -208,14 +207,13 @@ public final class KnownNetwork implements Parcelable {
KnownNetwork other = (KnownNetwork) obj;
return mNetworkSource == other.getNetworkSource()
&& Objects.equals(mSsid, other.getSsid())
- && Arrays.equals(mSecurityTypes, other.getSecurityTypes())
+ && Objects.equals(mSecurityTypes, other.getSecurityTypes())
&& Objects.equals(mDeviceInfo, other.getDeviceInfo());
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkSource, mSsid, Arrays.hashCode(mSecurityTypes),
- mDeviceInfo.hashCode());
+ return Objects.hash(mNetworkSource, mSsid, mSecurityTypes, mDeviceInfo);
}
@Override
@@ -227,7 +225,7 @@ public final class KnownNetwork implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mNetworkSource);
dest.writeString(mSsid);
- dest.writeIntArray(mSecurityTypes);
+ dest.writeArraySet(mSecurityTypes);
mDeviceInfo.writeToParcel(dest, flags);
}
@@ -238,7 +236,8 @@ public final class KnownNetwork implements Parcelable {
*/
@NonNull
public static KnownNetwork readFromParcel(@NonNull Parcel in) {
- return new KnownNetwork(in.readInt(), in.readString(), in.createIntArray(),
+ return new KnownNetwork(in.readInt(), in.readString(),
+ (ArraySet<Integer>) in.readArraySet(null),
DeviceInfo.readFromParcel(in));
}
@@ -260,7 +259,7 @@ public final class KnownNetwork implements Parcelable {
return new StringBuilder("KnownNetwork[")
.append("NetworkSource=").append(mNetworkSource)
.append(", ssid=").append(mSsid)
- .append(", securityTypes=").append(Arrays.toString(mSecurityTypes))
+ .append(", securityTypes=").append(mSecurityTypes.toString())
.append(", deviceInfo=").append(mDeviceInfo.toString())
.append("]").toString();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index d5d01d38ef62..a19510b3f9df 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -192,9 +192,9 @@ public class SharedConnectivityManager {
mService = ISharedConnectivityService.Stub.asInterface(service);
if (!mCallbackProxyCache.isEmpty()) {
synchronized (mCallbackProxyCache) {
- mCallbackProxyCache.keySet().forEach(callback -> {
- registerCallbackInternal(callback, mCallbackProxyCache.get(callback));
- });
+ mCallbackProxyCache.keySet().forEach(callback ->
+ registerCallbackInternal(
+ callback, mCallbackProxyCache.get(callback)));
mCallbackProxyCache.clear();
}
}
@@ -258,7 +258,9 @@ public class SharedConnectivityManager {
}
/**
- * Registers a callback for receiving updates to the list of Tether Networks and Known Networks.
+ * Registers a callback for receiving updates to the list of Tether Networks, Known Networks,
+ * shared connectivity settings state, tether network connection status and known network
+ * connection status.
* The {@link SharedConnectivityClientCallback#onRegisterCallbackFailed} will be called if the
* registration failed.
*
@@ -418,4 +420,104 @@ public class SharedConnectivityManager {
}
return true;
}
+
+ /**
+ * Gets the list of tether networks the user can select to connect to.
+ *
+ * @return Returns a {@link List} of {@link TetherNetwork} objects, empty list on failure.
+ */
+ @NonNull
+ public List<TetherNetwork> getTetherNetworks() {
+ if (mService == null) {
+ return List.of();
+ }
+
+ try {
+ return mService.getTetherNetworks();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getTetherNetworks", e);
+ }
+ return List.of();
+ }
+
+ /**
+ * Gets the list of known networks the user can select to connect to.
+ *
+ * @return Returns a {@link List} of {@link KnownNetwork} objects, empty list on failure.
+ */
+ @NonNull
+ public List<KnownNetwork> getKnownNetworks() {
+ if (mService == null) {
+ return List.of();
+ }
+
+ try {
+ return mService.getKnownNetworks();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getKnownNetworks", e);
+ }
+ return List.of();
+ }
+
+ /**
+ * Gets the shared connectivity settings state.
+ *
+ * @return Returns a {@link SharedConnectivitySettingsState} object with the state, null on
+ * failure.
+ */
+ @Nullable
+ public SharedConnectivitySettingsState getSettingsState() {
+ if (mService == null) {
+ return null;
+ }
+
+ try {
+ return mService.getSettingsState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getSettingsState", e);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the connection status of the tether network the user selected to connect to.
+ *
+ * @return Returns a {@link TetherNetworkConnectionStatus} object with the connection status,
+ * null on failure. If no connection is active the status will be
+ * {@link TetherNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}.
+ */
+ @Nullable
+ public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() {
+ if (mService == null) {
+ return null;
+ }
+
+ try {
+ return mService.getTetherNetworkConnectionStatus();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getTetherNetworkConnectionStatus", e);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the connection status of the known network the user selected to connect to.
+ *
+ * @return Returns a {@link KnownNetworkConnectionStatus} object with the connection status,
+ * null on failure. If no connection is active the status will be
+ * {@link KnownNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}.
+ */
+ @Nullable
+ public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() {
+ if (mService == null) {
+ return null;
+ }
+
+ try {
+ return mService.getKnownNetworkConnectionStatus();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in getKnownNetworkConnectionStatus", e);
+ }
+ return null;
+ }
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
index af4fd4a2cc76..7b591d3a45bd 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
@@ -25,12 +25,12 @@ import android.annotation.SystemApi;
import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
import android.os.Parcel;
import android.os.Parcelable;
-
+import android.util.ArraySet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Objects;
+import java.util.Set;
/**
* A data class representing an Instant Tether network.
@@ -79,7 +79,7 @@ public final class TetherNetwork implements Parcelable {
private final String mNetworkName;
@Nullable private final String mHotspotSsid;
@Nullable private final String mHotspotBssid;
- @Nullable @SecurityType private final int[] mHotspotSecurityTypes;
+ @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes;
/**
* Builder class for {@link TetherNetwork}.
@@ -91,9 +91,8 @@ public final class TetherNetwork implements Parcelable {
private String mNetworkName;
@Nullable private String mHotspotSsid;
@Nullable private String mHotspotBssid;
- @Nullable @SecurityType private int[] mHotspotSecurityTypes;
-
- public Builder() {}
+ @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes =
+ new ArraySet<>();
/**
* Set the remote device ID.
@@ -168,15 +167,14 @@ public final class TetherNetwork implements Parcelable {
}
/**
- * Sets the hotspot security types supported by the remote device, or null if hotspot is
- * off.
+ * Adds a security type supported by the hotspot created by the remote device.
*
- * @param hotspotSecurityTypes The array of security types supported by the hotspot.
+ * @param hotspotSecurityType A security type supported by the hotspot.
* @return Returns the Builder object.
*/
@NonNull
- public Builder setHotspotSecurityTypes(@NonNull @SecurityType int[] hotspotSecurityTypes) {
- mHotspotSecurityTypes = hotspotSecurityTypes;
+ public Builder addHotspotSecurityType(@SecurityType int hotspotSecurityType) {
+ mHotspotSecurityTypes.add(hotspotSecurityType);
return this;
}
@@ -218,7 +216,7 @@ public final class TetherNetwork implements Parcelable {
@NonNull String networkName,
@Nullable String hotspotSsid,
@Nullable String hotspotBssid,
- @Nullable @SecurityType int[] hotspotSecurityTypes) {
+ @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) {
validate(deviceId,
networkType,
networkName);
@@ -228,7 +226,7 @@ public final class TetherNetwork implements Parcelable {
mNetworkName = networkName;
mHotspotSsid = hotspotSsid;
mHotspotBssid = hotspotBssid;
- mHotspotSecurityTypes = hotspotSecurityTypes;
+ mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes);
}
/**
@@ -293,11 +291,11 @@ public final class TetherNetwork implements Parcelable {
/**
* Gets the hotspot security types supported by the remote device.
*
- * @return Returns the array of security types supported by the hotspot.
+ * @return Returns a set of the security types supported by the hotspot.
*/
- @Nullable
+ @NonNull
@SecurityType
- public int[] getHotspotSecurityTypes() {
+ public Set<Integer> getHotspotSecurityTypes() {
return mHotspotSecurityTypes;
}
@@ -311,13 +309,13 @@ public final class TetherNetwork implements Parcelable {
&& Objects.equals(mNetworkName, other.getNetworkName())
&& Objects.equals(mHotspotSsid, other.getHotspotSsid())
&& Objects.equals(mHotspotBssid, other.getHotspotBssid())
- && Arrays.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes());
+ && Objects.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes());
}
@Override
public int hashCode() {
return Objects.hash(mDeviceId, mDeviceInfo, mNetworkName, mHotspotSsid, mHotspotBssid,
- Arrays.hashCode(mHotspotSecurityTypes));
+ mHotspotSecurityTypes);
}
@Override
@@ -333,7 +331,7 @@ public final class TetherNetwork implements Parcelable {
dest.writeString(mNetworkName);
dest.writeString(mHotspotSsid);
dest.writeString(mHotspotBssid);
- dest.writeIntArray(mHotspotSecurityTypes);
+ dest.writeArraySet(mHotspotSecurityTypes);
}
/**
@@ -345,7 +343,7 @@ public final class TetherNetwork implements Parcelable {
public static TetherNetwork readFromParcel(@NonNull Parcel in) {
return new TetherNetwork(in.readLong(), DeviceInfo.readFromParcel(in),
in.readInt(), in.readString(), in.readString(), in.readString(),
- in.createIntArray());
+ (ArraySet<Integer>) in.readArraySet(null));
}
@NonNull
@@ -370,7 +368,7 @@ public final class TetherNetwork implements Parcelable {
.append(", networkName=").append(mNetworkName)
.append(", hotspotSsid=").append(mHotspotSsid)
.append(", hotspotBssid=").append(mHotspotBssid)
- .append(", hotspotSecurityTypes=").append(Arrays.toString(mHotspotSecurityTypes))
+ .append(", hotspotSecurityTypes=").append(mHotspotSecurityTypes.toString())
.append("]").toString();
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
index 52da596081a4..9f33e99ad3d1 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
@@ -18,6 +18,9 @@ package android.net.wifi.sharedconnectivity.service;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
+import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
/*
@@ -30,4 +33,9 @@ interface ISharedConnectivityService {
void disconnectTetherNetwork(in TetherNetwork network);
void connectKnownNetwork(in KnownNetwork network);
void forgetKnownNetwork(in KnownNetwork network);
+ List<TetherNetwork> getTetherNetworks();
+ List<KnownNetwork> getKnownNetworks();
+ SharedConnectivitySettingsState getSettingsState();
+ TetherNetworkConnectionStatus getTetherNetworkConnectionStatus();
+ KnownNetworkConnectionStatus getKnownNetworkConnectionStatus();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index ff7246f46859..d2cea613d2ec 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -33,16 +33,15 @@ import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
import android.net.wifi.sharedconnectivity.app.TetherNetwork;
import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
@@ -61,30 +60,21 @@ public abstract class SharedConnectivityService extends Service {
private static final boolean DEBUG = true;
private Handler mHandler;
- private final List<ISharedConnectivityCallback> mCallbacks = new ArrayList<>();
- // Used to find DeathRecipient when unregistering a callback to call unlinkToDeath.
- private final Map<ISharedConnectivityCallback, DeathRecipient> mDeathRecipientMap =
- new HashMap<>();
-
+ private final RemoteCallbackList<ISharedConnectivityCallback> mRemoteCallbackList =
+ new RemoteCallbackList<>();
private List<TetherNetwork> mTetherNetworks = Collections.emptyList();
private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
- private SharedConnectivitySettingsState mSettingsState;
- private TetherNetworkConnectionStatus mTetherNetworkConnectionStatus;
- private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus;
-
- private final class DeathRecipient implements IBinder.DeathRecipient {
- ISharedConnectivityCallback mCallback;
-
- DeathRecipient(ISharedConnectivityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void binderDied() {
- mCallbacks.remove(mCallback);
- mDeathRecipientMap.remove(mCallback);
- }
- }
+ private SharedConnectivitySettingsState mSettingsState =
+ new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(false)
+ .setExtras(Bundle.EMPTY).build();
+ private TetherNetworkConnectionStatus mTetherNetworkConnectionStatus =
+ new TetherNetworkConnectionStatus.Builder()
+ .setStatus(TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
+ .setExtras(Bundle.EMPTY).build();
+ private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus =
+ new KnownNetworkConnectionStatus.Builder()
+ .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
+ .setExtras(Bundle.EMPTY).build();
@Override
@Nullable
@@ -128,12 +118,49 @@ public abstract class SharedConnectivityService extends Service {
mHandler.post(() -> onForgetKnownNetwork(network));
}
+ @Override
+ public List<TetherNetwork> getTetherNetworks() {
+ checkPermissions();
+ return mTetherNetworks;
+ }
+
+ @Override
+ public List<KnownNetwork> getKnownNetworks() {
+ checkPermissions();
+ return mKnownNetworks;
+ }
+
+ @Override
+ public SharedConnectivitySettingsState getSettingsState() {
+ checkPermissions();
+ return mSettingsState;
+ }
+
+ @Override
+ public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() {
+ checkPermissions();
+ return mTetherNetworkConnectionStatus;
+ }
+
+ @Override
+ public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() {
+ checkPermissions();
+ return mKnownNetworkConnectionStatus;
+ }
+
@RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ /**
+ * checkPermissions is using checkCallingOrSelfPermission to support CTS testing of this
+ * service. This does allow a process to bind to itself if it holds the proper
+ * permission. We do not consider this to be an issue given that the process can already
+ * access the service data since they are in the same process.
+ */
private void checkPermissions() {
- if (checkCallingPermission(NETWORK_SETTINGS) != PackageManager.PERMISSION_GRANTED
- && checkCallingPermission(NETWORK_SETUP_WIZARD)
- != PackageManager.PERMISSION_GRANTED) {
+ if (checkCallingOrSelfPermission(NETWORK_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED
+ && checkCallingOrSelfPermission(NETWORK_SETUP_WIZARD)
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Calling process must have NETWORK_SETTINGS or"
+ " NETWORK_SETUP_WIZARD permission");
}
@@ -148,85 +175,13 @@ public abstract class SharedConnectivityService extends Service {
public void onBind() {}
private void onRegisterCallback(ISharedConnectivityCallback callback) {
- // Listener gets triggered on first register using cashed data
- if (!notifyTetherNetworkUpdate(callback) || !notifyKnownNetworkUpdate(callback)
- || !notifySettingsStateUpdate(callback)
- || !notifyTetherNetworkConnectionStatusChanged(callback)
- || !notifyKnownNetworkConnectionStatusChanged(callback)) {
- if (DEBUG) Log.w(TAG, "Failed to notify client");
- return;
- }
-
- DeathRecipient deathRecipient = new DeathRecipient(callback);
- try {
- callback.asBinder().linkToDeath(deathRecipient, 0);
- mCallbacks.add(callback);
- mDeathRecipientMap.put(callback, deathRecipient);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in registerCallback", e);
- }
+ mRemoteCallbackList.register(callback);
}
private void onUnregisterCallback(ISharedConnectivityCallback callback) {
- DeathRecipient deathRecipient = mDeathRecipientMap.get(callback);
- if (deathRecipient != null) {
- callback.asBinder().unlinkToDeath(deathRecipient, 0);
- mDeathRecipientMap.remove(callback);
- }
- mCallbacks.remove(callback);
+ mRemoteCallbackList.unregister(callback);
}
- private boolean notifyTetherNetworkUpdate(ISharedConnectivityCallback callback) {
- try {
- callback.onTetherNetworksUpdated(mTetherNetworks);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in notifyTetherNetworkUpdate", e);
- return false;
- }
- return true;
- }
-
- private boolean notifyKnownNetworkUpdate(ISharedConnectivityCallback callback) {
- try {
- callback.onKnownNetworksUpdated(mKnownNetworks);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in notifyKnownNetworkUpdate", e);
- return false;
- }
- return true;
- }
-
- private boolean notifySettingsStateUpdate(ISharedConnectivityCallback callback) {
- try {
- callback.onSharedConnectivitySettingsChanged(mSettingsState);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in notifySettingsStateUpdate", e);
- return false;
- }
- return true;
- }
-
- private boolean notifyTetherNetworkConnectionStatusChanged(
- ISharedConnectivityCallback callback) {
- try {
- callback.onTetherNetworkConnectionStatusChanged(mTetherNetworkConnectionStatus);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in notifyTetherNetworkConnectionStatusChanged", e);
- return false;
- }
- return true;
- }
-
- private boolean notifyKnownNetworkConnectionStatusChanged(
- ISharedConnectivityCallback callback) {
- try {
- callback.onKnownNetworkConnectionStatusChanged(mKnownNetworkConnectionStatus);
- } catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in notifyKnownNetworkConnectionStatusChanged", e);
- return false;
- }
- return true;
- }
/**
* Implementing application should call this method to provide an up-to-date list of Tether
* Networks to be displayed to the user.
@@ -239,9 +194,15 @@ public abstract class SharedConnectivityService extends Service {
public final void setTetherNetworks(@NonNull List<TetherNetwork> networks) {
mTetherNetworks = networks;
- for (ISharedConnectivityCallback callback:mCallbacks) {
- notifyTetherNetworkUpdate(callback);
+ int count = mRemoteCallbackList.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ try {
+ mRemoteCallbackList.getBroadcastItem(i).onTetherNetworksUpdated(mTetherNetworks);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in setTetherNetworks", e);
+ }
}
+ mRemoteCallbackList.finishBroadcast();
}
/**
@@ -256,9 +217,15 @@ public abstract class SharedConnectivityService extends Service {
public final void setKnownNetworks(@NonNull List<KnownNetwork> networks) {
mKnownNetworks = networks;
- for (ISharedConnectivityCallback callback:mCallbacks) {
- notifyKnownNetworkUpdate(callback);
+ int count = mRemoteCallbackList.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ try {
+ mRemoteCallbackList.getBroadcastItem(i).onKnownNetworksUpdated(mKnownNetworks);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in setKnownNetworks", e);
+ }
}
+ mRemoteCallbackList.finishBroadcast();
}
/**
@@ -274,9 +241,16 @@ public abstract class SharedConnectivityService extends Service {
public final void setSettingsState(@NonNull SharedConnectivitySettingsState settingsState) {
mSettingsState = settingsState;
- for (ISharedConnectivityCallback callback:mCallbacks) {
- notifySettingsStateUpdate(callback);
+ int count = mRemoteCallbackList.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ try {
+ mRemoteCallbackList.getBroadcastItem(i).onSharedConnectivitySettingsChanged(
+ mSettingsState);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in setSettingsState", e);
+ }
}
+ mRemoteCallbackList.finishBroadcast();
}
/**
@@ -289,9 +263,18 @@ public abstract class SharedConnectivityService extends Service {
public final void updateTetherNetworkConnectionStatus(
@NonNull TetherNetworkConnectionStatus status) {
mTetherNetworkConnectionStatus = status;
- for (ISharedConnectivityCallback callback:mCallbacks) {
- notifyTetherNetworkConnectionStatusChanged(callback);
+
+ int count = mRemoteCallbackList.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ try {
+ mRemoteCallbackList
+ .getBroadcastItem(i).onTetherNetworkConnectionStatusChanged(
+ mTetherNetworkConnectionStatus);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in updateTetherNetworkConnectionStatus", e);
+ }
}
+ mRemoteCallbackList.finishBroadcast();
}
/**
@@ -305,9 +288,17 @@ public abstract class SharedConnectivityService extends Service {
@NonNull KnownNetworkConnectionStatus status) {
mKnownNetworkConnectionStatus = status;
- for (ISharedConnectivityCallback callback:mCallbacks) {
- notifyKnownNetworkConnectionStatusChanged(callback);
+ int count = mRemoteCallbackList.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ try {
+ mRemoteCallbackList
+ .getBroadcastItem(i).onKnownNetworkConnectionStatusChanged(
+ mKnownNetworkConnectionStatus);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in updateKnownNetworkConnectionStatus", e);
+ }
}
+ mRemoteCallbackList.finishBroadcast();
}
/**
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
index f8f07008e34b..e6595eb2e2a3 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
@@ -19,8 +19,7 @@ package android.net.wifi.sharedconnectivity.app;
import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_LAPTOP;
import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
@@ -29,7 +28,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
- * Unit tests for {@link android.app.sharedconnectivity.DeviceInfo}.
+ * Unit tests for {@link DeviceInfo}.
*/
@SmallTest
public class DeviceInfoTest {
@@ -63,8 +62,8 @@ public class DeviceInfoTest {
parcelR.setDataPosition(0);
DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcelR);
- assertEquals(info, fromParcel);
- assertEquals(info.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(info);
+ assertThat(fromParcel.hashCode()).isEqualTo(info.hashCode());
}
/**
@@ -74,24 +73,24 @@ public class DeviceInfoTest {
public void testEqualsOperation() {
DeviceInfo info1 = buildDeviceInfoBuilder().build();
DeviceInfo info2 = buildDeviceInfoBuilder().build();
- assertEquals(info1, info2);
+ assertThat(info1).isEqualTo(info2);
DeviceInfo.Builder builder = buildDeviceInfoBuilder().setDeviceType(DEVICE_TYPE_1);
- assertNotEquals(info1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(info1);
builder = buildDeviceInfoBuilder().setDeviceName(DEVICE_NAME_1);
- assertNotEquals(info1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(info1);
builder = buildDeviceInfoBuilder().setModelName(DEVICE_MODEL_1);
- assertNotEquals(info1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(info1);
builder = buildDeviceInfoBuilder()
.setBatteryPercentage(BATTERY_PERCENTAGE_1);
- assertNotEquals(info1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(info1);
builder = buildDeviceInfoBuilder()
.setConnectionStrength(CONNECTION_STRENGTH_1);
- assertNotEquals(info1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(info1);
}
/**
@@ -100,12 +99,19 @@ public class DeviceInfoTest {
@Test
public void testGetMethods() {
DeviceInfo info = buildDeviceInfoBuilder().build();
- assertEquals(info.getDeviceType(), DEVICE_TYPE);
- assertEquals(info.getDeviceName(), DEVICE_NAME);
- assertEquals(info.getModelName(), DEVICE_MODEL);
- assertEquals(info.getBatteryPercentage(), BATTERY_PERCENTAGE);
- assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH);
- assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH);
+ assertThat(info.getDeviceType()).isEqualTo(DEVICE_TYPE);
+ assertThat(info.getDeviceName()).isEqualTo(DEVICE_NAME);
+ assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL);
+ assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE);
+ assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH);
+ }
+
+ @Test
+ public void testHashCode() {
+ DeviceInfo info1 = buildDeviceInfoBuilder().build();
+ DeviceInfo info2 = buildDeviceInfoBuilder().build();
+
+ assertThat(info1.hashCode()).isEqualTo(info2.hashCode());
}
private DeviceInfo.Builder buildDeviceInfoBuilder() {
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
index 37dca8def0bd..8a0f21e5eea6 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
@@ -22,8 +22,7 @@ import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURC
import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED;
import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVE_FAILED;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import android.os.Parcel;
@@ -32,8 +31,10 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
+import java.util.Arrays;
+
/**
- * Unit tests for {@link android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus}.
+ * Unit tests for {@link KnownNetworkConnectionStatus}.
*/
@SmallTest
public class KnownNetworkConnectionStatusTest {
@@ -45,6 +46,7 @@ public class KnownNetworkConnectionStatusTest {
.setConnectionStrength(2).setBatteryPercentage(50).build();
private static final String SSID_1 = "TEST_SSID1";
private static final String BUNDLE_KEY = "INT-KEY";
+ private static final int BUNDLE_VALUE = 1;
/**
* Verifies parcel serialization/deserialization.
@@ -64,8 +66,8 @@ public class KnownNetworkConnectionStatusTest {
KnownNetworkConnectionStatus fromParcel =
KnownNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
- assertEquals(status, fromParcel);
- assertEquals(status.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(status);
+ assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode());
}
/**
@@ -75,15 +77,15 @@ public class KnownNetworkConnectionStatusTest {
public void testEqualsOperation() {
KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
- assertEquals(status2, status2);
+ assertThat(status1).isEqualTo(status2);
KnownNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
.setStatus(CONNECTION_STATUS_SAVE_FAILED);
- assertNotEquals(status1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(status1);
builder = buildConnectionStatusBuilder()
.setKnownNetwork(buildKnownNetworkBuilder().setSsid(SSID_1).build());
- assertNotEquals(status1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(status1);
}
/**
@@ -92,9 +94,17 @@ public class KnownNetworkConnectionStatusTest {
@Test
public void testGetMethods() {
KnownNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
- assertEquals(status.getStatus(), CONNECTION_STATUS_SAVED);
- assertEquals(status.getKnownNetwork(), buildKnownNetworkBuilder().build());
- assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY));
+ assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_SAVED);
+ assertThat(status.getKnownNetwork()).isEqualTo(buildKnownNetworkBuilder().build());
+ assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
+ }
+
+ @Test
+ public void testHashCode() {
+ KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
+ KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
+
+ assertThat(status1.hashCode()).isEqualTo(status2.hashCode());
}
private KnownNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
@@ -106,13 +116,15 @@ public class KnownNetworkConnectionStatusTest {
private Bundle buildBundle() {
Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, 1);
+ bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
return bundle;
}
private KnownNetwork.Builder buildKnownNetworkBuilder() {
- return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID)
- .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO);
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
+ return builder;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
index 266afcc9a1a6..872dd2e63227 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
@@ -23,18 +23,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import java.util.Arrays;
+
/**
- * Unit tests for {@link android.app.sharedconnectivity.KnownNetwork}.
+ * Unit tests for {@link KnownNetwork}.
*/
@SmallTest
public class KnownNetworkTest {
@@ -69,8 +70,8 @@ public class KnownNetworkTest {
parcelR.setDataPosition(0);
KnownNetwork fromParcel = KnownNetwork.CREATOR.createFromParcel(parcelR);
- assertEquals(network, fromParcel);
- assertEquals(network.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(network);
+ assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode());
}
/**
@@ -80,20 +81,21 @@ public class KnownNetworkTest {
public void testEqualsOperation() {
KnownNetwork network1 = buildKnownNetworkBuilder().build();
KnownNetwork network2 = buildKnownNetworkBuilder().build();
- assertEquals(network1, network2);
+ assertThat(network1).isEqualTo(network2);
KnownNetwork.Builder builder = buildKnownNetworkBuilder()
.setNetworkSource(NETWORK_SOURCE_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildKnownNetworkBuilder().setSsid(SSID_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildKnownNetworkBuilder().setSecurityTypes(SECURITY_TYPES_1);
- assertNotEquals(network1, builder.build());
+ builder = buildKnownNetworkBuilder();
+ Arrays.stream(SECURITY_TYPES_1).forEach(builder::addSecurityType);
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildKnownNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
}
/**
@@ -102,14 +104,27 @@ public class KnownNetworkTest {
@Test
public void testGetMethods() {
KnownNetwork network = buildKnownNetworkBuilder().build();
- assertEquals(network.getNetworkSource(), NETWORK_SOURCE);
- assertEquals(network.getSsid(), SSID);
- assertArrayEquals(network.getSecurityTypes(), SECURITY_TYPES);
- assertEquals(network.getDeviceInfo(), DEVICE_INFO);
+ ArraySet<Integer> securityTypes = new ArraySet<>();
+ Arrays.stream(SECURITY_TYPES).forEach(securityTypes::add);
+
+ assertThat(network.getNetworkSource()).isEqualTo(NETWORK_SOURCE);
+ assertThat(network.getSsid()).isEqualTo(SSID);
+ assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes);
+ assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO);
+ }
+
+ @Test
+ public void testHashCode() {
+ KnownNetwork network1 = buildKnownNetworkBuilder().build();
+ KnownNetwork network2 = buildKnownNetworkBuilder().build();
+
+ assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
}
private KnownNetwork.Builder buildKnownNetworkBuilder() {
- return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID)
- .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO);
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
+ return builder;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
index 439d456d7657..7c0a8b65813c 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -22,9 +22,8 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doThrow;
@@ -36,6 +35,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.net.wifi.sharedconnectivity.service.ISharedConnectivityService;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
@@ -46,6 +46,8 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -95,6 +97,7 @@ public class SharedConnectivityManagerTest {
@Test
public void bindingToService() {
SharedConnectivityManager.create(mContext);
+
verify(mContext).bindService(any(), any(), anyInt());
}
@@ -104,7 +107,8 @@ public class SharedConnectivityManagerTest {
@Test
public void resourcesNotDefined() {
when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException());
- assertNull(SharedConnectivityManager.create(mContext));
+
+ assertThat(SharedConnectivityManager.create(mContext)).isNull();
}
/**
@@ -115,8 +119,10 @@ public class SharedConnectivityManagerTest {
throws Exception {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+
// Since the binder is embedded in a proxy class, the call to registerCallback is done on
// the proxy. So instead verifying that the proxy is calling the binder.
verify(mIBinder).transact(anyInt(), any(Parcel.class), any(Parcel.class), anyInt());
@@ -126,9 +132,11 @@ public class SharedConnectivityManagerTest {
public void registerCallback_serviceNotConnected_canUnregisterAndReregister() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.unregisterCallback(mClientCallback);
manager.registerCallback(mExecutor, mClientCallback);
+
verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
}
@@ -136,7 +144,9 @@ public class SharedConnectivityManagerTest {
public void registerCallback_serviceConnected() throws Exception {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.registerCallback(mExecutor, mClientCallback);
+
verify(mService).registerCallback(any());
verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
}
@@ -145,8 +155,10 @@ public class SharedConnectivityManagerTest {
public void registerCallback_doubleRegistration_shouldFail() throws Exception {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.registerCallback(mExecutor, mClientCallback);
+
verify(mClientCallback).onRegisterCallbackFailed(any(IllegalStateException.class));
}
@@ -155,7 +167,9 @@ public class SharedConnectivityManagerTest {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
doThrow(new RemoteException()).when(mService).registerCallback(any());
+
manager.registerCallback(mExecutor, mClientCallback);
+
verify(mClientCallback).onRegisterCallbackFailed(any(RemoteException.class));
}
@@ -166,22 +180,26 @@ public class SharedConnectivityManagerTest {
public void unregisterCallback_withoutRegisteringFirst_serviceNotConnected_shouldFail() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
public void unregisterCallback_withoutRegisteringFirst_serviceConnected_shouldFail() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- assertFalse(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
public void unregisterCallback() throws Exception {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.registerCallback(mExecutor, mClientCallback);
- assertTrue(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isTrue();
verify(mService).unregisterCallback(any());
}
@@ -189,26 +207,32 @@ public class SharedConnectivityManagerTest {
public void unregisterCallback_doubleUnregistration_serviceConnected_shouldFail() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.unregisterCallback(mClientCallback);
- assertFalse(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
public void unregisterCallback_doubleUnregistration_serviceNotConnected_shouldFail() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.unregisterCallback(mClientCallback);
- assertFalse(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
public void unregisterCallback_remoteException_shouldFail() throws Exception {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
doThrow(new RemoteException()).when(mService).unregisterCallback(any());
- assertFalse(manager.unregisterCallback(mClientCallback));
+
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
/**
@@ -217,16 +241,20 @@ public class SharedConnectivityManagerTest {
@Test
public void onServiceConnected_registerCallbackBeforeConnection() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+
verify(mClientCallback).onServiceConnected();
}
@Test
public void onServiceConnected_registerCallbackAfterConnection() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+
manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
manager.registerCallback(mExecutor, mClientCallback);
+
verify(mClientCallback).onServiceConnected();
}
@@ -236,18 +264,22 @@ public class SharedConnectivityManagerTest {
@Test
public void onServiceDisconnected_registerCallbackBeforeConnection() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+
manager.registerCallback(mExecutor, mClientCallback);
manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
manager.getServiceConnection().onServiceDisconnected(COMPONENT_NAME);
+
verify(mClientCallback).onServiceDisconnected();
}
@Test
public void onServiceDisconnected_registerCallbackAfterConnection() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+
manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
manager.registerCallback(mExecutor, mClientCallback);
manager.getServiceConnection().onServiceDisconnected(COMPONENT_NAME);
+
verify(mClientCallback).onServiceDisconnected();
}
@@ -259,7 +291,8 @@ public class SharedConnectivityManagerTest {
TetherNetwork network = buildTetherNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.connectTetherNetwork(network));
+
+ assertThat(manager.connectTetherNetwork(network)).isFalse();
}
@Test
@@ -267,7 +300,9 @@ public class SharedConnectivityManagerTest {
TetherNetwork network = buildTetherNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.connectTetherNetwork(network);
+
verify(mService).connectTetherNetwork(network);
}
@@ -277,7 +312,8 @@ public class SharedConnectivityManagerTest {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
doThrow(new RemoteException()).when(mService).connectTetherNetwork(network);
- assertFalse(manager.connectTetherNetwork(network));
+
+ assertThat(manager.connectTetherNetwork(network)).isFalse();
}
/**
@@ -288,7 +324,8 @@ public class SharedConnectivityManagerTest {
TetherNetwork network = buildTetherNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.disconnectTetherNetwork(network));
+
+ assertThat(manager.disconnectTetherNetwork(network)).isFalse();
}
@Test
@@ -296,7 +333,9 @@ public class SharedConnectivityManagerTest {
TetherNetwork network = buildTetherNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.disconnectTetherNetwork(network);
+
verify(mService).disconnectTetherNetwork(network);
}
@@ -306,7 +345,8 @@ public class SharedConnectivityManagerTest {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any());
- assertFalse(manager.disconnectTetherNetwork(network));
+
+ assertThat(manager.disconnectTetherNetwork(network)).isFalse();
}
/**
@@ -317,7 +357,8 @@ public class SharedConnectivityManagerTest {
KnownNetwork network = buildKnownNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.connectKnownNetwork(network));
+
+ assertThat(manager.connectKnownNetwork(network)).isFalse();
}
@Test
@@ -325,7 +366,9 @@ public class SharedConnectivityManagerTest {
KnownNetwork network = buildKnownNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.connectKnownNetwork(network);
+
verify(mService).connectKnownNetwork(network);
}
@@ -335,7 +378,8 @@ public class SharedConnectivityManagerTest {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
doThrow(new RemoteException()).when(mService).connectKnownNetwork(network);
- assertFalse(manager.connectKnownNetwork(network));
+
+ assertThat(manager.connectKnownNetwork(network)).isFalse();
}
/**
@@ -346,7 +390,8 @@ public class SharedConnectivityManagerTest {
KnownNetwork network = buildKnownNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.forgetKnownNetwork(network));
+
+ assertThat(manager.forgetKnownNetwork(network)).isFalse();
}
@Test
@@ -354,7 +399,9 @@ public class SharedConnectivityManagerTest {
KnownNetwork network = buildKnownNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
+
manager.forgetKnownNetwork(network);
+
verify(mService).forgetKnownNetwork(network);
}
@@ -364,7 +411,158 @@ public class SharedConnectivityManagerTest {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network);
- assertFalse(manager.forgetKnownNetwork(network));
+
+ assertThat(manager.forgetKnownNetwork(network)).isFalse();
+ }
+
+ /**
+ * Verify getters.
+ */
+ @Test
+ public void getTetherNetworks_serviceNotConnected_shouldReturnEmptyList() {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(null);
+
+ assertThat(manager.getKnownNetworks()).isEmpty();
+ }
+
+ @Test
+ public void getTetherNetworks_remoteException_shouldReturnEmptyList() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).getTetherNetworks();
+
+ assertThat(manager.getKnownNetworks()).isEmpty();
+ }
+
+ @Test
+ public void getTetherNetworks_shouldReturnNetworksList() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ List<TetherNetwork> networks = List.of(buildTetherNetwork());
+ manager.setService(mService);
+ when(mService.getTetherNetworks()).thenReturn(networks);
+
+ assertThat(manager.getTetherNetworks()).containsExactly(buildTetherNetwork());
+ }
+
+ @Test
+ public void getKnownNetworks_serviceNotConnected_shouldReturnEmptyList()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(null);
+
+ assertThat(manager.getKnownNetworks()).isEmpty();
+ }
+
+ @Test
+ public void getKnownNetworks_remoteException_shouldReturnEmptyList() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).getKnownNetworks();
+
+ assertThat(manager.getKnownNetworks()).isEmpty();
+ }
+
+ @Test
+ public void getKnownNetworks_shouldReturnNetworksList() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ List<KnownNetwork> networks = List.of(buildKnownNetwork());
+ manager.setService(mService);
+ when(mService.getKnownNetworks()).thenReturn(networks);
+
+ assertThat(manager.getKnownNetworks()).containsExactly(buildKnownNetwork());
+ }
+
+ @Test
+ public void getSettingsState_serviceNotConnected_shouldReturnNull() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(null);
+
+ assertThat(manager.getSettingsState()).isNull();
+ }
+
+ @Test
+ public void getSettingsState_remoteException_shouldReturnNull() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).getSettingsState();
+
+ assertThat(manager.getSettingsState()).isNull();
+ }
+
+ @Test
+ public void getSettingsState_serviceConnected_shouldReturnState() throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ SharedConnectivitySettingsState state = new SharedConnectivitySettingsState.Builder()
+ .setInstantTetherEnabled(true).setExtras(new Bundle()).build();
+ manager.setService(mService);
+ when(mService.getSettingsState()).thenReturn(state);
+
+ assertThat(manager.getSettingsState()).isEqualTo(state);
+ }
+
+ @Test
+ public void getTetherNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(null);
+
+ assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getTetherNetworkConnectionStatus_remoteException_shouldReturnNull()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).getTetherNetworkConnectionStatus();
+
+ assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getTetherNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ TetherNetworkConnectionStatus status = new TetherNetworkConnectionStatus.Builder()
+ .setStatus(TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT)
+ .setExtras(new Bundle()).build();
+ manager.setService(mService);
+ when(mService.getTetherNetworkConnectionStatus()).thenReturn(status);
+
+ assertThat(manager.getTetherNetworkConnectionStatus()).isEqualTo(status);
+ }
+
+ @Test
+ public void getKnownNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(null);
+
+ assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getKnownNetworkConnectionStatus_remoteException_shouldReturnNull()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).getKnownNetworkConnectionStatus();
+
+ assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getKnownNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
+ throws RemoteException {
+ SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+ KnownNetworkConnectionStatus status = new KnownNetworkConnectionStatus.Builder()
+ .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED)
+ .setExtras(new Bundle()).build();
+ manager.setService(mService);
+ when(mService.getKnownNetworkConnectionStatus()).thenReturn(status);
+
+ assertThat(manager.getKnownNetworkConnectionStatus()).isEqualTo(status);
}
private void setResources(@Mock Context context) {
@@ -374,18 +572,20 @@ public class SharedConnectivityManagerTest {
}
private TetherNetwork buildTetherNetwork() {
- return new TetherNetwork.Builder()
+ TetherNetwork.Builder builder = new TetherNetwork.Builder()
.setDeviceId(DEVICE_ID)
.setDeviceInfo(DEVICE_INFO)
.setNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
- .setHotspotSsid(HOTSPOT_SSID)
- .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES)
- .build();
+ .setHotspotSsid(HOTSPOT_SSID);
+ Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+ return builder.build();
}
private KnownNetwork buildKnownNetwork() {
- return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID)
- .setSecurityTypes(SECURITY_TYPES).build();
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
+ return builder.build();
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
index 3137c7268ae0..752b74905c97 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
@@ -16,8 +16,7 @@
package android.net.wifi.sharedconnectivity.app;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
@@ -26,7 +25,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
/**
- * Unit tests for {@link android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState}.
+ * Unit tests for {@link SharedConnectivitySettingsState}.
*/
@SmallTest
public class SharedConnectivitySettingsStateTest {
@@ -51,8 +50,8 @@ public class SharedConnectivitySettingsStateTest {
SharedConnectivitySettingsState fromParcel =
SharedConnectivitySettingsState.CREATOR.createFromParcel(parcelR);
- assertEquals(state, fromParcel);
- assertEquals(state.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(state);
+ assertThat(fromParcel.hashCode()).isEqualTo(state.hashCode());
}
/**
@@ -62,11 +61,11 @@ public class SharedConnectivitySettingsStateTest {
public void testEqualsOperation() {
SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build();
SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build();
- assertEquals(state1, state2);
+ assertThat(state1).isEqualTo(state2);
SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder()
.setInstantTetherEnabled(INSTANT_TETHER_STATE_1);
- assertNotEquals(state1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(state1);
}
/**
@@ -75,7 +74,15 @@ public class SharedConnectivitySettingsStateTest {
@Test
public void testGetMethods() {
SharedConnectivitySettingsState state = buildSettingsStateBuilder().build();
- assertEquals(state.isInstantTetherEnabled(), INSTANT_TETHER_STATE);
+ assertThat(state.isInstantTetherEnabled()).isEqualTo(INSTANT_TETHER_STATE);
+ }
+
+ @Test
+ public void testHashCode() {
+ SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build();
+ SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build();
+
+ assertThat(state1.hashCode()).isEqualTo(state2.hashCode());
}
private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder() {
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java
index 1d9c2e6df38a..0844364e7a63 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java
@@ -23,8 +23,7 @@ import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE
import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT;
import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import android.os.Parcel;
@@ -33,8 +32,10 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
+import java.util.Arrays;
+
/**
- * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus}.
+ * Unit tests for {@link TetherNetworkConnectionStatus}.
*/
@SmallTest
public class TetherNetworkConnectionStatusTest {
@@ -49,6 +50,7 @@ public class TetherNetworkConnectionStatusTest {
private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
private static final long DEVICE_ID_1 = 111L;
private static final String BUNDLE_KEY = "INT-KEY";
+ private static final int BUNDLE_VALUE = 1;
/**
* Verifies parcel serialization/deserialization.
@@ -68,8 +70,8 @@ public class TetherNetworkConnectionStatusTest {
TetherNetworkConnectionStatus fromParcel =
TetherNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
- assertEquals(status, fromParcel);
- assertEquals(status.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(status);
+ assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode());
}
/**
@@ -79,15 +81,15 @@ public class TetherNetworkConnectionStatusTest {
public void testEqualsOperation() {
TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
- assertEquals(status2, status2);
+ assertThat(status1).isEqualTo(status2);
TetherNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
.setStatus(CONNECTION_STATUS_TETHERING_TIMEOUT);
- assertNotEquals(status1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(status1);
builder = buildConnectionStatusBuilder()
.setTetherNetwork(buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1).build());
- assertNotEquals(status1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(status1);
}
/**
@@ -96,13 +98,20 @@ public class TetherNetworkConnectionStatusTest {
@Test
public void testGetMethods() {
TetherNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
- assertEquals(status.getStatus(), CONNECTION_STATUS_ENABLING_HOTSPOT);
- assertEquals(status.getTetherNetwork(), buildTetherNetworkBuilder().build());
- assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY));
+ assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_ENABLING_HOTSPOT);
+ assertThat(status.getTetherNetwork()).isEqualTo(buildTetherNetworkBuilder().build());
+ assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
}
- private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
+ @Test
+ public void testHashCode() {
+ TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
+ TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
+
+ assertThat(status1.hashCode()).isEqualTo(status2.hashCode());
+ }
+ private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
return new TetherNetworkConnectionStatus.Builder()
.setStatus(CONNECTION_STATUS_ENABLING_HOTSPOT)
.setTetherNetwork(buildTetherNetworkBuilder().build())
@@ -111,18 +120,19 @@ public class TetherNetworkConnectionStatusTest {
private Bundle buildBundle() {
Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, 1);
+ bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
return bundle;
}
private TetherNetwork.Builder buildTetherNetworkBuilder() {
- return new TetherNetwork.Builder()
+ TetherNetwork.Builder builder = new TetherNetwork.Builder()
.setDeviceId(DEVICE_ID)
.setDeviceInfo(DEVICE_INFO)
.setNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
- .setHotspotBssid(HOTSPOT_BSSID)
- .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES);
+ .setHotspotBssid(HOTSPOT_BSSID);
+ Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+ return builder;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
index b01aec4ad1c1..a50d76782c4a 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
@@ -24,18 +24,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB
import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_WIFI;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import java.util.Arrays;
+
/**
- * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetwork}.
+ * Unit tests for {@link TetherNetwork}.
*/
@SmallTest
public class TetherNetworkTest {
@@ -76,8 +77,8 @@ public class TetherNetworkTest {
parcelR.setDataPosition(0);
TetherNetwork fromParcel = TetherNetwork.CREATOR.createFromParcel(parcelR);
- assertEquals(network, fromParcel);
- assertEquals(network.hashCode(), fromParcel.hashCode());
+ assertThat(fromParcel).isEqualTo(network);
+ assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode());
}
/**
@@ -87,28 +88,31 @@ public class TetherNetworkTest {
public void testEqualsOperation() {
TetherNetwork network1 = buildTetherNetworkBuilder().build();
TetherNetwork network2 = buildTetherNetworkBuilder().build();
- assertEquals(network1, network2);
+ assertThat(network1).isEqualTo(network2);
TetherNetwork.Builder builder = buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildTetherNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildTetherNetworkBuilder().setNetworkType(NETWORK_TYPE_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildTetherNetworkBuilder().setNetworkName(NETWORK_NAME_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildTetherNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
builder = buildTetherNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder.build()).isNotEqualTo(network1);
+
+ builder = buildTetherNetworkBuilder();
+ TetherNetwork.Builder builder1 = buildTetherNetworkBuilder();
+ Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
- builder = buildTetherNetworkBuilder().setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES_1);
- assertNotEquals(network1, builder.build());
+ assertThat(builder1.build()).isNotEqualTo(builder.build());
}
/**
@@ -117,23 +121,35 @@ public class TetherNetworkTest {
@Test
public void testGetMethods() {
TetherNetwork network = buildTetherNetworkBuilder().build();
- assertEquals(network.getDeviceId(), DEVICE_ID);
- assertEquals(network.getDeviceInfo(), DEVICE_INFO);
- assertEquals(network.getNetworkType(), NETWORK_TYPE);
- assertEquals(network.getNetworkName(), NETWORK_NAME);
- assertEquals(network.getHotspotSsid(), HOTSPOT_SSID);
- assertEquals(network.getHotspotBssid(), HOTSPOT_BSSID);
- assertArrayEquals(network.getHotspotSecurityTypes(), HOTSPOT_SECURITY_TYPES);
+ ArraySet<Integer> securityTypes = new ArraySet<>();
+ Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
+
+ assertThat(network.getDeviceId()).isEqualTo(DEVICE_ID);
+ assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO);
+ assertThat(network.getNetworkType()).isEqualTo(NETWORK_TYPE);
+ assertThat(network.getNetworkName()).isEqualTo(NETWORK_NAME);
+ assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID);
+ assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID);
+ assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes);
+ }
+
+ @Test
+ public void testHashCode() {
+ TetherNetwork network1 = buildTetherNetworkBuilder().build();
+ TetherNetwork network2 = buildTetherNetworkBuilder().build();
+
+ assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
}
private TetherNetwork.Builder buildTetherNetworkBuilder() {
- return new TetherNetwork.Builder()
+ TetherNetwork.Builder builder = new TetherNetwork.Builder()
.setDeviceId(DEVICE_ID)
.setDeviceInfo(DEVICE_INFO)
.setNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
- .setHotspotBssid(HOTSPOT_BSSID)
- .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES);
+ .setHotspotBssid(HOTSPOT_BSSID);
+ Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+ return builder;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index d7f7fea4df3e..81efa79f6df8 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -16,14 +16,29 @@
package android.net.wifi.sharedconnectivity.service;
-import static org.junit.Assert.assertNotNull;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
+import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED;
+import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
+import android.net.wifi.sharedconnectivity.app.DeviceInfo;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
+import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
+import android.os.Bundle;
import android.os.Looper;
+import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
@@ -33,11 +48,37 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
- * Unit tests for {@link android.net.wifi.sharedconnectivity.service.SharedConnectivityService}.
+ * Unit tests for {@link SharedConnectivityService}.
*/
@SmallTest
public class SharedConnectivityServiceTest {
+ private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final TetherNetwork TETHER_NETWORK =
+ new TetherNetwork.Builder().setDeviceId(1).setDeviceInfo(DEVICE_INFO)
+ .setNetworkType(NETWORK_TYPE_CELLULAR).setNetworkName("TEST_NETWORK")
+ .setHotspotSsid("TEST_SSID").setHotspotBssid("TEST_BSSID")
+ .addHotspotSecurityType(SECURITY_TYPE_WEP)
+ .addHotspotSecurityType(SECURITY_TYPE_EAP).build();
+ private static final List<TetherNetwork> TETHER_NETWORKS = List.of(TETHER_NETWORK);
+ private static final KnownNetwork KNOWN_NETWORK =
+ new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE_NEARBY_SELF)
+ .setSsid("TEST_SSID").addSecurityType(SECURITY_TYPE_WEP)
+ .addSecurityType(SECURITY_TYPE_EAP).setDeviceInfo(DEVICE_INFO).build();
+ private static final List<KnownNetwork> KNOWN_NETWORKS = List.of(KNOWN_NETWORK);
+ private static final SharedConnectivitySettingsState SETTINGS_STATE =
+ new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true)
+ .setExtras(Bundle.EMPTY).build();
+ private static final TetherNetworkConnectionStatus TETHER_NETWORK_CONNECTION_STATUS =
+ new TetherNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_UNKNOWN)
+ .setTetherNetwork(TETHER_NETWORK).setExtras(Bundle.EMPTY).build();
+ private static final KnownNetworkConnectionStatus KNOWN_NETWORK_CONNECTION_STATUS =
+ new KnownNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_SAVED)
+ .setKnownNetwork(KNOWN_NETWORK).setExtras(Bundle.EMPTY).build();
@Mock
Context mContext;
@@ -66,20 +107,70 @@ public class SharedConnectivityServiceTest {
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
}
- /**
- * Verifies service returns
- */
@Test
- public void testOnBind() {
+ public void onBind_isNotNull() {
+ SharedConnectivityService service = createService();
+
+ assertThat(service.onBind(new Intent())).isNotNull();
+ }
+
+ @Test
+ public void getTetherNetworks() throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ service.setTetherNetworks(TETHER_NETWORKS);
+
+ assertThat(binder.getTetherNetworks())
+ .containsExactlyElementsIn(List.copyOf(TETHER_NETWORKS));
+ }
+
+ @Test
+ public void getKnownNetworks() throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ service.setKnownNetworks(KNOWN_NETWORKS);
+
+ assertThat(binder.getKnownNetworks())
+ .containsExactlyElementsIn(List.copyOf(KNOWN_NETWORKS));
+ }
+
+ @Test
+ public void getSharedConnectivitySettingsState() throws RemoteException {
SharedConnectivityService service = createService();
- assertNotNull(service.onBind(new Intent()));
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ service.setSettingsState(SETTINGS_STATE);
+
+ assertThat(binder.getSettingsState()).isEqualTo(SETTINGS_STATE);
}
@Test
- public void testCallbacks() {
+ public void updateTetherNetworkConnectionStatus() throws RemoteException {
SharedConnectivityService service = createService();
ISharedConnectivityService.Stub binder =
(ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ service.updateTetherNetworkConnectionStatus(TETHER_NETWORK_CONNECTION_STATUS);
+
+ assertThat(binder.getTetherNetworkConnectionStatus())
+ .isEqualTo(TETHER_NETWORK_CONNECTION_STATUS);
+ }
+
+ @Test
+ public void updateKnownNetworkConnectionStatus() throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ service.updateKnownNetworkConnectionStatus(KNOWN_NETWORK_CONNECTION_STATUS);
+
+ assertThat(binder.getKnownNetworkConnectionStatus())
+ .isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS);
}
private SharedConnectivityService createService() {